From: chris@kneesaa.uk.xensource.com Date: Wed, 12 Jul 2006 18:16:10 +0000 (+0100) Subject: Add qemu 0.8.1 based ioemu. X-Git-Tag: archive/raspbian/4.8.0-1+rpi1~1^2~15862 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/success//%22http:/www.example.com/cgi/success/?a=commitdiff_plain;h=8f4b90220c984d0a6e207cf91ec2b08d2ece19d4;p=xen.git Add qemu 0.8.1 based ioemu. Signed-off-by: Christian Limpach --- diff --git a/.hgignore b/.hgignore index f083160029..19b1c556d0 100644 --- a/.hgignore +++ b/.hgignore @@ -123,12 +123,18 @@ ^tools/firmware/vmxassist/roms\.h$ ^tools/firmware/vmxassist/vmxassist$ ^tools/firmware/vmxassist/vmxloader$ -^tools/ioemu/config-host\..*$ -^tools/ioemu/keysym_adapter_sdl\.h$ -^tools/ioemu/keysym_adapter_vnc\.h$ -^tools/ioemu/target-.*/Makefile$ -^tools/ioemu/target-.*/config\..*$ -^tools/ioemu/target-.*/qemu-dm$ +^tools/ioemu/\.pc/.*$ +^tools/ioemu/config-host\.h$ +^tools/ioemu/config-host\.mak$ +^tools/ioemu/i386-dm/config\.h$ +^tools/ioemu/i386-dm/config\.mak$ +^tools/ioemu/i386-dm/qemu-dm$ +^tools/ioemu/qemu-doc\.html$ +^tools/ioemu/qemu-img\.1$ +^tools/ioemu/qemu-img\.pod$ +^tools/ioemu/qemu-tech\.html$ +^tools/ioemu/qemu\.1$ +^tools/ioemu/qemu\.pod$ ^tools/libxc/xen/.*$ ^tools/misc/cpuperf/cpuperf-perfcntr$ ^tools/misc/cpuperf/cpuperf-xen$ diff --git a/tools/ioemu/.cvsignore b/tools/ioemu/.cvsignore new file mode 100644 index 0000000000..101139fc8d --- /dev/null +++ b/tools/ioemu/.cvsignore @@ -0,0 +1,29 @@ +arm-user +arm-softmmu +armeb-user +config-host.* +dyngen +i386 +i386-softmmu +i386-user +ppc-softmmu +ppc64-softmmu +ppc-user +qemu-doc.html +qemu-tech.html +qemu.1 +qemu.pod +qemu-img.1 +qemu-img.pod +sparc-user +qemu-img +sparc-softmmu +x86_64-softmmu +sparc64-user +sparc64-softmmu +mips-softmmu +mipsel-softmmu +mips-user +mipsel-user +sh4-user +sh4-softmmu diff --git a/tools/ioemu/COPYING b/tools/ioemu/COPYING new file mode 100644 index 0000000000..e77696ae8d --- /dev/null +++ b/tools/ioemu/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/tools/ioemu/COPYING.LIB b/tools/ioemu/COPYING.LIB new file mode 100644 index 0000000000..223ede7de3 --- /dev/null +++ b/tools/ioemu/COPYING.LIB @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/tools/ioemu/CVS/Entries b/tools/ioemu/CVS/Entries new file mode 100644 index 0000000000..43bfd4c3ee --- /dev/null +++ b/tools/ioemu/CVS/Entries @@ -0,0 +1,109 @@ +D/audio//// +D/hw//// +D/linux-user//// +D/pc-bios//// +D/slirp//// +D/target-arm//// +D/target-i386//// +D/target-ppc//// +D/target-sparc//// +D/tests//// +D/fpu//// +D/keymaps//// +D/target-mips//// +/.cvsignore/1.13/Thu May 25 18:22:25 2006//Trelease_0_8_1 +/COPYING/1.1/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/COPYING.LIB/1.2/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/Changelog/1.116/Thu May 25 18:22:25 2006//Trelease_0_8_1 +/LICENSE/1.2/Sat Feb 12 14:45:23 2005//Trelease_0_8_1 +/Makefile/1.99/Thu May 25 18:22:25 2006//Trelease_0_8_1 +/Makefile.target/1.107/Thu May 25 18:22:25 2006//Trelease_0_8_1 +/README/1.12/Wed May 24 10:40:08 2006//Trelease_0_8_1 +/README.distrib/1.2/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/TODO/1.38/Thu May 25 18:22:25 2006//Trelease_0_8_1 +/VERSION/1.28/Thu May 25 18:22:25 2006//Trelease_0_8_1 +/a.out.h/1.2/Wed May 24 10:40:08 2006//Trelease_0_8_1 +/aes.c/1.1/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/aes.h/1.1/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/alpha-dis.c/1.3/Thu May 25 18:22:25 2006//Trelease_0_8_1 +/alpha.ld/1.1/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/arm-dis.c/1.3/Wed May 24 10:40:08 2006//Trelease_0_8_1 +/arm.ld/1.1/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/block-bochs.c/1.1/Tue Apr 26 21:34:00 2005//Trelease_0_8_1 +/block-cloop.c/1.3/Thu May 25 18:22:25 2006//Trelease_0_8_1 +/block-cow.c/1.5/Thu May 25 12:38:49 2006//Trelease_0_8_1 +/block-dmg.c/1.4/Thu May 25 18:22:25 2006//Trelease_0_8_1 +/block-qcow.c/1.6/Thu May 25 18:22:25 2006//Trelease_0_8_1 +/block-vmdk.c/1.7/Thu May 25 18:22:25 2006//Trelease_0_8_1 +/block-vpc.c/1.2/Thu May 25 12:38:49 2006//Trelease_0_8_1 +/block-vvfat.c/1.5/Thu May 25 18:22:25 2006//Trelease_0_8_1 +/block.c/1.26/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/block_int.h/1.4/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/bswap.h/1.5/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/cocoa.m/1.8/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/configure/1.100/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/console.c/1.5/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/cpu-all.h/1.54/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/cpu-defs.h/1.15/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/cpu-exec.c/1.78/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/dis-asm.h/1.11/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/disas.c/1.30/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/disas.h/1.7/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/dyngen-exec.h/1.27/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/dyngen-op.h/1.1/Mon Jan 3 23:40:55 2005//Trelease_0_8_1 +/dyngen.c/1.42/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/dyngen.h/1.10/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/elf.h/1.6/Thu May 25 12:38:50 2006//Trelease_0_8_1 +/elf_ops.h/1.3/Tue May 2 20:54:12 2006//Trelease_0_8_1 +/exec-all.h/1.47/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/exec.c/1.79/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/gdbstub.c/1.36/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/gdbstub.h/1.2/Tue Apr 26 20:42:36 2005//Trelease_0_8_1 +/i386-dis.c/1.5/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/i386-vl.ld/1.3/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/i386.ld/1.2/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/ia64.ld/1.1/Thu Apr 7 22:20:28 2005//Trelease_0_8_1 +/keymaps.c/1.2/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/kqemu.c/1.10/Thu May 25 18:22:26 2006//Trelease_0_8_1 +/kqemu.h/1.1/Wed Feb 8 22:39:17 2006//Trelease_0_8_1 +/linux-2.6.9-qemu-fast.patch/1.1/Wed Dec 8 23:48:11 2004//Trelease_0_8_1 +/loader.c/1.2/Wed Apr 26 22:05:26 2006//Trelease_0_8_1 +/m68k-dis.c/1.1/Sun Nov 6 16:52:11 2005//Trelease_0_8_1 +/m68k.ld/1.1/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/mips-dis.c/1.3/Thu May 25 18:22:28 2006//Trelease_0_8_1 +/monitor.c/1.48/Thu May 25 18:22:28 2006//Trelease_0_8_1 +/osdep.c/1.10/Wed May 24 10:40:10 2006//Trelease_0_8_1 +/osdep.h/1.5/Wed May 24 10:40:10 2006//Trelease_0_8_1 +/ppc-dis.c/1.7/Thu May 25 12:38:51 2006//Trelease_0_8_1 +/ppc.ld/1.2/Thu May 25 12:38:51 2006//Trelease_0_8_1 +/qemu-binfmt-conf.sh/1.4/Thu May 25 18:22:28 2006//Trelease_0_8_1 +/qemu-doc.texi/1.87/Thu May 25 18:22:29 2006//Trelease_0_8_1 +/qemu-img.c/1.8/Thu May 25 12:38:51 2006//Trelease_0_8_1 +/qemu-img.texi/1.2/Thu May 25 12:38:51 2006//Trelease_0_8_1 +/qemu-tech.texi/1.9/Thu May 25 18:22:29 2006//Trelease_0_8_1 +/qemu_socket.h/1.1/Sun Apr 30 22:53:25 2006//Trelease_0_8_1 +/readline.c/1.1/Tue May 23 19:16:56 2006//Trelease_0_8_1 +/s390.ld/1.1/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/sdl.c/1.26/Thu May 25 18:22:29 2006//Trelease_0_8_1 +/sdl_keysym.h/1.3/Tue Mar 1 21:43:41 2005//Trelease_0_8_1 +/sh4-dis.c/1.1/Thu Apr 27 21:05:14 2006//Trelease_0_8_1 +/softmmu_exec.h/1.1/Sun Oct 30 18:16:26 2005//Trelease_0_8_1 +/softmmu_header.h/1.13/Thu May 25 18:22:29 2006//Trelease_0_8_1 +/softmmu_template.h/1.16/Thu May 25 18:22:29 2006//Trelease_0_8_1 +/sparc-dis.c/1.2/Thu May 25 18:22:29 2006//Trelease_0_8_1 +/sparc.ld/1.1/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/tap-win32.c/1.2/Sun Feb 19 12:40:00 2006//Trelease_0_8_1 +/texi2pod.pl/1.1/Wed May 17 14:47:02 2006//Trelease_0_8_1 +/thunk.c/1.6/Wed May 24 10:40:10 2006//Trelease_0_8_1 +/thunk.h/1.13/Wed May 17 14:47:02 2006//Trelease_0_8_1 +/translate-all.c/1.14/Thu May 25 18:22:29 2006//Trelease_0_8_1 +/translate-op.c/1.1/Sun Mar 13 16:53:06 2005//Trelease_0_8_1 +/usb-linux.c/1.4/Sat Mar 11 18:03:38 2006//Trelease_0_8_1 +/vgafont.h/1.1/Wed May 17 14:47:02 2006//Trelease_0_8_1 +/vl.c/1.184/Thu May 25 18:22:30 2006//Trelease_0_8_1 +/vl.h/1.116/Thu May 25 18:22:30 2006//Trelease_0_8_1 +/vnc.c/1.4/Mon May 1 21:44:22 2006//Trelease_0_8_1 +/vnc_keysym.h/1.1/Sun Apr 30 21:28:36 2006//Trelease_0_8_1 +/vnchextile.h/1.1/Sun Apr 30 21:28:36 2006//Trelease_0_8_1 +/x86_64.ld/1.1/Thu Jan 6 20:50:00 2005//Trelease_0_8_1 +D/target-sh4//// diff --git a/tools/ioemu/CVS/Repository b/tools/ioemu/CVS/Repository new file mode 100644 index 0000000000..1750fe828d --- /dev/null +++ b/tools/ioemu/CVS/Repository @@ -0,0 +1 @@ +qemu diff --git a/tools/ioemu/CVS/Root b/tools/ioemu/CVS/Root new file mode 100644 index 0000000000..e8a9815e6a --- /dev/null +++ b/tools/ioemu/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.savannah.nongnu.org:/sources/qemu diff --git a/tools/ioemu/CVS/Tag b/tools/ioemu/CVS/Tag new file mode 100644 index 0000000000..31c251a4bf --- /dev/null +++ b/tools/ioemu/CVS/Tag @@ -0,0 +1 @@ +Nrelease_0_8_1 diff --git a/tools/ioemu/Changelog b/tools/ioemu/Changelog new file mode 100644 index 0000000000..659b5017ba --- /dev/null +++ b/tools/ioemu/Changelog @@ -0,0 +1,376 @@ +version 0.8.1: + + - USB tablet support (Brad Campbell, Anthony Liguori) + - win32 host serial support (Kazu) + - PC speaker support (Joachim Henke) + - IDE LBA48 support (Jens Axboe) + - SSE3 support + - Solaris port (Ben Taylor) + - Preliminary SH4 target (Samuel Tardieu) + - VNC server (Anthony Liguori) + - slirp fixes (Ed Swierk et al.) + - USB fixes + - ARM Versatile Platform Baseboard emulation (Paul Brook) + +version 0.8.0: + + - ARM system emulation: Arm Integrator/CP board with an arm1026ej-s + cpu (Paul Brook) + - SMP support + - Mac OS X cocoa improvements (Mike Kronenberg) + - Mac OS X CoreAudio driver (Mike Kronenberg) + - DirectSound driver (malc) + - ALSA audio driver (malc) + - new audio options: '-soundhw' and '-audio-help' (malc) + - ES1370 PCI audio device (malc) + - Initial USB support + - Linux host serial port access + - Linux host low level parallel port access + - New network emulation code supporting VLANs. + - MIPS and MIPSel User Linux emulation + - MIPS fixes to boot Linux (Daniel Jacobowitz) + - NX bit support + - Initial SPARC SMP support (Blue Swirl) + - Major overhaul of the virtual FAT driver for read/write support + (Johannes Schindelin) + +version 0.7.2: + + - x86_64 fixes (Win2000 and Linux 2.6 boot in 32 bit) + - merge self modifying code handling in dirty ram page mecanism. + - MIPS fixes (Ralf Baechle) + - better user net performances + +version 0.7.1: + + - read-only Virtual FAT support (Johannes Schindelin) + - Windows 2000 install disk full hack (original idea from Vladimir + N. Oleynik) + - VMDK disk image creation (Filip Navara) + - SPARC64 progress (Blue Swirl) + - initial MIPS support (Jocelyn mayer) + - MIPS improvements (Ralf Baechle) + - 64 bit fixes in user networking (initial patch by Gwenole Beauchesne) + - IOAPIC support (Filip Navara) + +version 0.7.0: + + - better BIOS translation and HDD geometry auto-detection + - user mode networking bug fix + - undocumented FPU ops support + - Cirrus VGA: support for 1280x1024x[8,15,16] modes + - 'pidfile' option + - .dmg disk image format support (Johannes Schindelin) + - keymaps support (initial patch by Johannes Schindelin) + - big endian ARM support (Lennert Buytenhek) + - added generic 64 bit target support + - x86_64 target support + - initial APIC support + - MMX/SSE/SSE2/PNI support + - PC parallel port support (Mark Jonckheere) + - initial SPARC64 support (Blue Swirl) + - SPARC target boots Linux (Blue Swirl) + - armv5te user mode support (Paul Brook) + - ARM VFP support (Paul Brook) + - ARM "Angel" semihosting syscalls (Paul Brook) + - user mode gdb stub support (Paul Brook) + - Samba 3 support + - initial Cocoa support (Pierre d'Herbemont) + - generic FPU emulation code + - Virtual PC read-only disk image support (Alex Beregszaszi) + +version 0.6.1: + + - Mac OS X port (Pierre d'Herbemont) + - Virtual console support + - Better monitor line edition + - New block device layer + - New 'qcow' growable disk image support with AES encryption and + transparent decompression + - VMware 3 and 4 read-only disk image support (untested) + - Support for up to 4 serial ports + - TFTP server support (Magnus Damm) + - Port redirection support in user mode networking + - Support for not executable data sections + - Compressed loop disk image support (Johannes Schindelin) + - Level triggered IRQ fix (aka NE2000 PCI performance fix) (Steve + Wormley) + - Fixed Fedora Core 2 problems (now you can run qemu without any + LD_ASSUME_KERNEL tricks on FC2) + - DHCP fix for Windows (accept DHCPREQUEST alone) + - SPARC system emulation (Blue Swirl) + - Automatic Samba configuration for host file access from Windows. + - '-loadvm' and '-full-screen' options + - ne2000 savevm support (Johannes Schindelin) + - Ctrl-Alt is now the default grab key. Ctrl-Alt-[0-9] switches to + the virtual consoles. + - BIOS floppy fix for NT4 (Mike Nordell, Derek Fawcus, Volker Ruppert) + - Floppy fixes for NT4 and NT5 (Mike Nordell) + - NT4 IDE fixes (Ben Pfaf, Mike Nordell) + - SDL Audio support and SB16 fixes (malc) + - ENTER instruction bug fix (initial patch by Stefan Kisdaroczi) + - VGA font change fix + - VGA read-only CRTC register fix + +version 0.6.0: + + - minimalist FPU exception support (NetBSD FPU probe fix) + - cr0.ET fix (Win95 boot) + - *BSD port (Markus Niemisto) + - I/O access fix (signaled by Mark Jonckheere) + - IDE drives serial number fix (Mike Nordell) + - int13 CDROM BIOS fix (aka Solaris x86 install CD fix) + - int15, ah=86 BIOS fix (aka Solaris x86 hardware probe hang up fix) + - BSR/BSF "undefined behaviour" fix + - vmdk2raw: convert VMware disk images to raw images + - PCI support + - NE2K PCI support + - dummy VGA PCI support + - VGA font selection fix (Daniel Serpell) + - PIC reset fix (Hidemi KAWAI) + - PIC spurious irq support (aka Solaris install bug) + - added '-localtime' option + - Cirrus CL-GD54xx VGA support (initial patch by Makoto Suzuki (suzu)) + - APM and system shutdown support + - Fixed system reset + - Support for other PC BIOSes + - Initial PowerMac hardware emulation + - PowerMac/PREP OpenFirmware compatible BIOS (Jocelyn Mayer) + - initial IDE BMDMA support (needed for Darwin x86) + - Set the default memory size for PC emulation to 128 MB + +version 0.5.5: + + - SDL full screen support (initial patch by malc) + - VGA support on PowerPC PREP + - VBE fixes (Matthew Mastracci) + - PIT fixes (aka Win98 hardware probe and "VGA slowness" bug) + - IDE master only fixes (aka Win98 CD-ROM probe bug) + - ARM load/store half word fix (Ulrich Hecht) + - FDC fixes for Win98 + +version 0.5.4: + + - qemu-fast fixes + - BIOS area protection fix (aka EMM386.EXE fix) (Mike Nordell) + - keyboard/mouse fix (Mike Nordell) + - IDE fixes (Linux did not recognized slave drivers) + - VM86 EIP masking fix (aka NT5 install fix) (Mike Nordell) + - QEMU can now boot a PowerPC Linux kernel (Jocelyn Mayer) + - User mode network stack + - imul imm8 fix + 0x82 opcode support (Hidemi KAWAI) + - precise self modifying code (aka BeOS install bug) + +version 0.5.3: + + - added Bochs VESA VBE support + - VGA memory map mode 3 access fix (OS/2 install fix) + - IDE fixes (Jens Axboe) + - CPU interrupt fixes + - fixed various TLB invalidation cases (NT install) + - fixed cr0.WP semantics (XP install) + - direct chaining support for SPARC and PowerPC (faster) + - ARM NWFPE support (initial patch by Ulrich Hecht) + - added specific x86 to x86 translator (close to native performance + in qemu-i386 and qemu-fast) + - shm syscalls support (Paul McKerras) + - added accurate CR0.MP/ME/TS emulation + - fixed DMA memory write access (Win95 boot floppy fix) + - graphical x86 linux loader + - command line monitor + - generic removable device support + - support of CD-ROM change + - multiple network interface support + - initial x86-64 host support (Gwenole Beauchesne) + - lret to outer priviledge fix (OS/2 install fix) + - task switch fixes (SkyOS boot) + - VM save/restore commands + - new timer API + - more precise RTC emulation (periodic timers + time updates) + - Win32 port (initial patch by Kazu) + +version 0.5.2: + + - improved soft MMU speed (assembly functions and specializing) + - improved multitasking speed by avoiding flushing TBs when + switching tasks + - improved qemu-fast speed + - improved self modifying code handling (big performance gain in + softmmu mode). + - fixed IO checking + - fixed CD-ROM detection (win98 install CD) + - fixed addseg real mode bug (GRUB boot fix) + - added ROM memory support (win98 boot) + - fixed 'call Ev' in case of paging exception + - updated the script 'qemu-binfmt-conf.sh' to use QEMU automagically + when launching executables for the supported target CPUs. + - PowerPC system emulation update (Jocelyn Mayer) + - PC floppy emulation and DMA fixes (Jocelyn Mayer) + - polled mode for PIC (Jocelyn Mayer) + - fixed PTE dirty bit handling + - fixed xadd same reg bug + - fixed cmpxchg exception safeness + - access to virtual memory in gdb stub + - task gate and NT flag fixes + - eflags optimisation fix for string operations + +version 0.5.1: + + - float access fixes when using soft mmu + - PC emulation support on PowerPC + - A20 support + - IDE CD-ROM emulation + - ARM fixes (Ulrich Hecht) + - SB16 emulation (malc) + - IRET and INT fixes in VM86 mode with IOPL=3 + - Port I/Os use TSS io map + - Full task switching/task gate support + - added verr, verw, arpl, fcmovxx + - PowerPC target support (Jocelyn Mayer) + - Major SPARC target fixes (dynamically linked programs begin to work) + +version 0.5.0: + + - full hardware level VGA emulation + - graphical display with SDL + - added PS/2 mouse and keyboard emulation + - popw (%esp) fix + - mov to/from segment data width fix + - added real mode support + - added Bochs BIOS and LGPL'ed VGA BIOS loader in qemu + - m68k host port (Richard Zidlicky) + - partial soft MMU support for memory mapped I/Os + - multi-target build + - fixed: no error code in hardware interrupts + - fixed: pop ss, mov ss, x and sti disable hardware irqs for the next insn + - correct single stepping thru string operations + - preliminary SPARC target support (Thomas M. Ogrisegg) + - tun-fd option (Rusty Russell) + - automatic IDE geometry detection + - renamed 'vl' to qemu[-fast] and user qemu to qemu-{cpu}. + - added man page + - added full soft mmu mode to launch unpatched OSes. + +version 0.4.3: + + - x86 exception fix in case of nop instruction. + - gcc 3.2.2 bug workaround (RedHat 9 fix) + - sparc and Alpha host fixes + - many ARM target fixes: 'ls' and 'bash' can be launched. + +version 0.4.2: + + - many exception handling fixes (can compile a Linux kernel inside vl) + - IDE emulation support + - initial GDB stub support + - deferred update support for disk images (Rusty Russell) + - accept User Mode Linux Copy On Write disk images + - SMP kernels can at least be booted + +version 0.4.1: + + - more accurate timer support in vl. + - more reliable NE2000 probe in vl. + - added 2.5.66 kernel in vl-test. + - added VLTMPDIR environment variable in vl. + +version 0.4: + + - initial support for ring 0 x86 processor emulation + - fixed signal handling for correct dosemu DPMI emulation + - fast x86 MMU emulation with mmap() + - fixed popl (%esp) case + - Linux kernel can be executed by QEMU with the 'vl' command. + +version 0.3: + + - initial support for ARM emulation + - added fnsave, frstor, fnstenv, fldenv FPU instructions + - added FPU register save in signal emulation + - initial ARM port + - Sparc and Alpha ports work on the regression test + - generic ioctl number conversion + - fixed ioctl type conversion + +version 0.2: + + - PowerPC disassembly and ELF symbols output (Rusty Russell) + - flock support (Rusty Russell) + - ugetrlimit support (Rusty Russell) + - fstat64 fix (Rusty Russell) + - initial Alpha port (Falk Hueffner) + - initial IA64 port (Matt Wilson) + - initial Sparc and Sparc64 port (David S. Miller) + - added HLT instruction + - LRET instruction fix. + - added GPF generation for I/Os. + - added INT3 and TF flag support. + - SHL instruction C flag fix. + - mmap emulation for host page size > 4KB + - self-modifying code support + - better VM86 support (dosemu works on non trivial programs) + - precise exception support (EIP is computed correctly in most cases) + - more precise LDT/GDT/IDT emulation + - faster segment load in vm86 mode + - direct chaining of basic blocks (faster emulation) + +version 0.1.6: + + - automatic library search system. QEMU can now work with unpatched + ELF dynamic loader and libc (Rusty Russell). + - ISO C warning fixes (Alistair Strachan) + - first self-virtualizable version (works only as long as the + translation cache is not flushed) + - RH9 fixes + +version 0.1.5: + + - ppc64 support + personality() patch (Rusty Russell) + - first Alpha CPU patches (Falk Hueffner) + - removed bfd.h dependancy + - fixed shrd, shld, idivl and divl on PowerPC. + - fixed buggy glibc PowerPC rint() function (test-i386 passes now on PowerPC). + +version 0.1.4: + + - more accurate VM86 emulation (can launch small DOS 16 bit + executables in wine). + - fixed push/pop fs/gs + - added iret instruction. + - added times() syscall and SIOCATMARK ioctl. + +version 0.1.3: + + - S390 support (Ulrich Weigand) + - glibc 2.3.x compile fix (Ulrich Weigand) + - socketcall endian fix (Ulrich Weigand) + - struct sockaddr endian fix (Ulrich Weigand) + - sendmsg/recvmsg endian fix (Ulrich Weigand) + - execve endian fix (Ulrich Weigand) + - fdset endian fix (Ulrich Weigand) + - partial setsockopt syscall support (Ulrich Weigand) + - more accurate pushf/popf emulation + - first partial vm86() syscall support (can be used with runcom example). + - added bound, cmpxchg8b, cpuid instructions + - added 16 bit addressing support/override for string operations + - poll() fix + +version 0.1.2: + + - compile fixes + - xlat instruction + - xchg instruction memory lock + - added simple vm86 example (not working with QEMU yet). The 54 byte + DOS executable 'pi_10.com' program was released by Bertram + Felgenhauer (more information at http://www.boo.net/~jasonp/pipage.html). + +version 0.1.1: + + - glibc 2.2 compilation fixes + - added -s and -L options + - binary distribution of x86 glibc and wine + - big endian fixes in ELF loader and getdents. + +version 0.1: + + - initial public release. diff --git a/tools/ioemu/LICENSE b/tools/ioemu/LICENSE new file mode 100644 index 0000000000..bfc9a1f427 --- /dev/null +++ b/tools/ioemu/LICENSE @@ -0,0 +1,12 @@ +The following points clarify the QEMU licenses: + +1) The QEMU virtual CPU core library (libqemu.a) and the QEMU PC + system emulator are released under the GNU Lesser General Public + License. + +2) The Linux user mode QEMU emulator is released under the GNU General + Public License. + +3) QEMU is a trademark of Fabrice Bellard. + +Fabrice Bellard. \ No newline at end of file diff --git a/tools/ioemu/Makefile b/tools/ioemu/Makefile new file mode 100644 index 0000000000..e1c6bf894a --- /dev/null +++ b/tools/ioemu/Makefile @@ -0,0 +1,149 @@ +XEN_ROOT=../.. +include $(XEN_ROOT)/tools/Rules.mk + +include config-host.mak + +CFLAGS+=-Wall -O2 -g -fno-strict-aliasing -I. +ifdef CONFIG_DARWIN +CFLAGS+= -mdynamic-no-pic +endif +LDFLAGS=-g +LIBS= +DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE +TOOLS=qemu-img$(EXESUF) +ifdef CONFIG_STATIC +LDFLAGS+=-static +endif +ifdef BUILD_DOCS +DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 +else +DOCS= +endif + +all: $(DOCS) + for d in $(TARGET_DIRS); do \ + $(MAKE) -C $$d $@ || exit 1 ; \ + done + +qemu-img$(EXESUF): qemu-img.c block.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c block-vvfat.c + $(CC) -DQEMU_TOOL $(CFLAGS) $(LDFLAGS) $(DEFINES) -o $@ $^ -lz $(LIBS) + +dyngen$(EXESUF): dyngen.c + $(HOST_CC) $(CFLAGS) $(DEFINES) -o $@ $^ + +clean: +# avoid old build problems by removing potentially incorrect old files + rm -f config.mak config.h op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h + rm -f *.o *.a $(TOOLS) dyngen$(EXESUF) TAGS *.pod *~ */*~ + $(MAKE) -C tests clean + for d in $(TARGET_DIRS); do \ + $(MAKE) -C $$d $@ || exit 1 ; \ + done + +distclean: clean + rm -f config-host.mak config-host.h $(DOCS) + for d in $(TARGET_DIRS); do \ + rm -rf $$d || exit 1 ; \ + done + +KEYMAPS=da en-gb et fr fr-ch is lt modifiers no pt-br sv \ +ar de en-us fi fr-be hr it lv nl pl ru th \ +common de-ch es fo fr-ca hu ja mk nl-be pt sl tr + +install-doc: $(DOCS) + mkdir -p "$(DESTDIR)$(docdir)" + $(INSTALL) -m 644 qemu-doc.html qemu-tech.html "$(DESTDIR)$(docdir)" +ifndef CONFIG_WIN32 + mkdir -p "$(DESTDIR)$(mandir)/man1" + $(INSTALL) qemu.1 qemu-img.1 "$(DESTDIR)$(mandir)/man1" +endif + +install: all $(if $(BUILD_DOCS),install-doc) + mkdir -p "$(DESTDIR)$(bindir)" +# $(INSTALL) -m 755 -s $(TOOLS) "$(DESTDIR)$(bindir)" +# mkdir -p "$(DESTDIR)$(datadir)" +# for x in bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \ +# video.x proll.elf linux_boot.bin; do \ +# $(INSTALL) -m 644 $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \ +# done +ifndef CONFIG_WIN32 + mkdir -p "$(DESTDIR)$(datadir)/keymaps" + for x in $(KEYMAPS); do \ + $(INSTALL) -m 644 $(SRC_PATH)/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \ + done +endif + for d in $(TARGET_DIRS); do \ + $(MAKE) -C $$d $@ || exit 1 ; \ + done + +# various test targets +test speed test2: all + $(MAKE) -C tests $@ + +TAGS: + etags *.[ch] tests/*.[ch] + +cscope: + rm -f ./cscope.* + find . -name "*.[ch]" -print > ./cscope.files + cscope -b + +# documentation +%.html: %.texi + texi2html -monolithic -number $< + +%.info: %.texi + makeinfo $< -o $@ + +%.dvi: %.texi + texi2dvi $< + +qemu.1: qemu-doc.texi + perl -w $(SRC_PATH)/texi2pod.pl $< qemu.pod + pod2man --section=1 --center=" " --release=" " qemu.pod > $@ + +qemu-img.1: qemu-img.texi + perl -w $(SRC_PATH)/texi2pod.pl $< qemu-img.pod + pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@ + +FILE=qemu-$(shell cat VERSION) + +# tar release (use 'make -k tar' on a checkouted tree) +tar: + rm -rf /tmp/$(FILE) + cp -r . /tmp/$(FILE) + ( cd /tmp ; tar zcvf ~/$(FILE).tar.gz $(FILE) --exclude CVS ) + rm -rf /tmp/$(FILE) + +# generate a binary distribution +tarbin: + ( cd / ; tar zcvf ~/qemu-$(VERSION)-i386.tar.gz \ + $(bindir)/qemu \ + $(bindir)/qemu-system-ppc \ + $(bindir)/qemu-system-sparc \ + $(bindir)/qemu-system-x86_64 \ + $(bindir)/qemu-system-mips \ + $(bindir)/qemu-system-mipsel \ + $(bindir)/qemu-system-arm \ + $(bindir)/qemu-i386 \ + $(bindir)/qemu-arm \ + $(bindir)/qemu-armeb \ + $(bindir)/qemu-sparc \ + $(bindir)/qemu-ppc \ + $(bindir)/qemu-mips \ + $(bindir)/qemu-mipsel \ + $(bindir)/qemu-img \ + $(datadir)/bios.bin \ + $(datadir)/vgabios.bin \ + $(datadir)/vgabios-cirrus.bin \ + $(datadir)/ppc_rom.bin \ + $(datadir)/video.x \ + $(datadir)/proll.elf \ + $(datadir)/linux_boot.bin \ + $(docdir)/qemu-doc.html \ + $(docdir)/qemu-tech.html \ + $(mandir)/man1/qemu.1 $(mandir)/man1/qemu-img.1 ) + +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/tools/ioemu/Makefile.target b/tools/ioemu/Makefile.target new file mode 100644 index 0000000000..253fef1b5d --- /dev/null +++ b/tools/ioemu/Makefile.target @@ -0,0 +1,531 @@ +include config.mak + +XEN_ROOT=../../.. +include $(XEN_ROOT)/tools/Rules.mk + +TARGET_BASE_ARCH:=$(TARGET_ARCH) +ifeq ($(TARGET_ARCH), x86_64) +TARGET_BASE_ARCH:=i386 +endif +ifeq ($(TARGET_ARCH), ppc64) +TARGET_BASE_ARCH:=ppc +endif +ifeq ($(TARGET_ARCH), sparc64) +TARGET_BASE_ARCH:=sparc +endif +TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH)$(TARGET_SUB) +VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/audio +DEFINES=-I. -I.. -I$(TARGET_PATH) -I$(SRC_PATH) +DEFINES+= -I$(XEN_ROOT)/tools/libxc +DEFINES+= -I$(XEN_ROOT)/tools/xenstore +ifdef CONFIG_USER_ONLY +VPATH+=:$(SRC_PATH)/linux-user +DEFINES+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ARCH) +endif +CFLAGS+=-Wall -O2 -g -fno-strict-aliasing +SSE2 := $(call test-gcc-flag,$(CC),-msse2) +ifeq ($(SSE2),-msse2) +CFLAGS += -DUSE_SSE2=1 -msse2 +endif +CFLAGS+= $(LOCAL_CFLAGS) +#CFLAGS+=-Werror +LDFLAGS=-g +LIBS= +HELPER_CFLAGS=$(CFLAGS) +DYNGEN=../dyngen$(EXESUF) +# user emulator name +TARGET_ARCH2=$(TARGET_ARCH) +ifeq ($(TARGET_ARCH),arm) + ifeq ($(TARGET_WORDS_BIGENDIAN),yes) + TARGET_ARCH2=armeb + endif +endif +ifeq ($(TARGET_ARCH),mips) + ifneq ($(TARGET_WORDS_BIGENDIAN),yes) + TARGET_ARCH2=mipsel + endif +endif +QEMU_USER=qemu-$(TARGET_ARCH2) +# system emulator name +ifdef CONFIG_SOFTMMU +ifeq ($(TARGET_ARCH), i386) +QEMU_SYSTEM=qemu$(EXESUF) +else +QEMU_SYSTEM=qemu-system-$(TARGET_ARCH2)$(EXESUF) +endif +else +QEMU_SYSTEM=qemu-fast +endif + +QEMU_SYSTEM=qemu-dm + +ifdef CONFIG_USER_ONLY +PROGS=$(QEMU_USER) +else +PROGS+=$(QEMU_SYSTEM) +ifndef CONFIG_SOFTMMU +CONFIG_STATIC=y +endif +endif # !CONFIG_USER_ONLY + +ifdef CONFIG_STATIC +LDFLAGS+=-static +endif + +ifeq ($(ARCH),i386) +CFLAGS+=-fomit-frame-pointer +OP_CFLAGS=$(CFLAGS) -mpreferred-stack-boundary=2 +ifeq ($(HAVE_GCC3_OPTIONS),yes) +OP_CFLAGS+= -falign-functions=0 -fno-gcse +else +OP_CFLAGS+= -malign-functions=0 +endif + +ifdef TARGET_GPROF +USE_I386_LD=y +endif +ifdef CONFIG_STATIC +USE_I386_LD=y +endif +ifdef USE_I386_LD +LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386.ld +else +# WARNING: this LDFLAGS is _very_ tricky : qemu is an ELF shared object +# that the kernel ELF loader considers as an executable. I think this +# is the simplest way to make it self virtualizable! +LDFLAGS+=-Wl,-shared +endif +endif + +ifeq ($(ARCH),x86_64) +OP_CFLAGS=$(CFLAGS) -falign-functions=0 +LDFLAGS+=-Wl,-T,$(SRC_PATH)/x86_64.ld +endif + +ifeq ($(ARCH),ppc) +CFLAGS+= -D__powerpc__ +OP_CFLAGS=$(CFLAGS) +LDFLAGS+=-Wl,-T,$(SRC_PATH)/ppc.ld +endif + +ifeq ($(ARCH),s390) +OP_CFLAGS=$(CFLAGS) +LDFLAGS+=-Wl,-T,$(SRC_PATH)/s390.ld +endif + +ifeq ($(ARCH),sparc) +CFLAGS+=-m32 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6 +LDFLAGS+=-m32 +OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0 +HELPER_CFLAGS=$(CFLAGS) -ffixed-i0 -mflat +# -static is used to avoid g1/g3 usage by the dynamic linker +LDFLAGS+=-Wl,-T,$(SRC_PATH)/sparc.ld -static +endif + +ifeq ($(ARCH),sparc64) +CFLAGS+=-m64 -ffixed-g1 -ffixed-g2 -ffixed-g3 -ffixed-g6 +LDFLAGS+=-m64 +OP_CFLAGS=$(CFLAGS) -fno-delayed-branch -ffixed-i0 +endif + +ifeq ($(ARCH),alpha) +# -msmall-data is not used because we want two-instruction relocations +# for the constant constructions +OP_CFLAGS=-Wall -O2 -g +# Ensure there's only a single GP +CFLAGS += -msmall-data +LDFLAGS+=-Wl,-T,$(SRC_PATH)/alpha.ld +endif + +ifeq ($(ARCH),ia64) +CFLAGS += -mno-sdata +OP_CFLAGS=$(CFLAGS) +LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld +endif + +ifeq ($(ARCH),arm) +OP_CFLAGS=$(CFLAGS) -mno-sched-prolog -fno-omit-frame-pointer +LDFLAGS+=-Wl,-T,$(SRC_PATH)/arm.ld +endif + +ifeq ($(ARCH),m68k) +OP_CFLAGS=$(CFLAGS) -fomit-frame-pointer +LDFLAGS+=-Wl,-T,m68k.ld +endif + +ifeq ($(HAVE_GCC3_OPTIONS),yes) +# very important to generate a return at the end of every operation +OP_CFLAGS+=-fno-reorder-blocks -fno-optimize-sibling-calls +endif + +ifeq ($(CONFIG_DARWIN),yes) +OP_CFLAGS+= -mdynamic-no-pic +LIBS+=-lmx +endif + +######################################################### + +DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE +LIBS+=-lm +LIBS+=-L../../libxc -lxenctrl -lxenguest +LIBS+=-L../../xenstore -lxenstore +LIBS+=-lpthread +ifndef CONFIG_USER_ONLY +LIBS+=-lz +endif +ifdef CONFIG_WIN32 +LIBS+=-lwinmm -lws2_32 -liphlpapi +endif +ifdef CONFIG_SOLARIS +LIBS+=-lsocket -lnsl -lresolv +endif + +# profiling code +ifdef TARGET_GPROF +LDFLAGS+=-p +main.o: CFLAGS+=-p +endif + +OBJS= elfload.o main.o syscall.o mmap.o signal.o path.o osdep.o thunk.o +ifeq ($(TARGET_ARCH), i386) +OBJS+= vm86.o +endif +ifeq ($(TARGET_ARCH), arm) +OBJS+=nwfpe/fpa11.o nwfpe/fpa11_cpdo.o \ +nwfpe/fpa11_cpdt.o nwfpe/fpa11_cprt.o nwfpe/fpopcode.o nwfpe/single_cpdo.o \ + nwfpe/double_cpdo.o nwfpe/extended_cpdo.o arm-semi.o +endif +SRCS:= $(OBJS:.o=.c) +OBJS+= libqemu.a + +# cpu emulator library +LIBOBJS=exec.o kqemu.o translate-op.o translate-all.o cpu-exec.o\ + translate.o op.o +ifdef CONFIG_SOFTFLOAT +LIBOBJS+=fpu/softfloat.o +else +LIBOBJS+=fpu/softfloat-native.o +endif +DEFINES+=-I$(SRC_PATH)/fpu + +ifeq ($(TARGET_ARCH), i386) +LIBOBJS+=helper.o helper2.o +ifeq ($(ARCH), i386) +LIBOBJS+=translate-copy.o +endif +endif + +ifeq ($(TARGET_ARCH), x86_64) +LIBOBJS+=helper.o helper2.o +endif + +ifeq ($(TARGET_BASE_ARCH), ppc) +LIBOBJS+= op_helper.o helper.o +endif + +ifeq ($(TARGET_ARCH), mips) +LIBOBJS+= op_helper.o helper.o +endif + +ifeq ($(TARGET_BASE_ARCH), sparc) +LIBOBJS+= op_helper.o helper.o +endif + +ifeq ($(TARGET_BASE_ARCH), arm) +LIBOBJS+= op_helper.o helper.o +endif + +ifeq ($(TARGET_BASE_ARCH), sh4) +LIBOBJS+= op_helper.o helper.o +endif + +# NOTE: the disassembler code is only needed for debugging +LIBOBJS+=disas.o +ifeq ($(findstring i386, $(TARGET_ARCH) $(ARCH)),i386) +USE_I386_DIS=y +endif +ifeq ($(findstring x86_64, $(TARGET_ARCH) $(ARCH)),x86_64) +USE_I386_DIS=y +endif +ifdef USE_I386_DIS +LIBOBJS+=i386-dis.o +endif +ifeq ($(findstring alpha, $(TARGET_ARCH) $(ARCH)),alpha) +LIBOBJS+=alpha-dis.o +endif +ifeq ($(findstring ppc, $(TARGET_BASE_ARCH) $(ARCH)),ppc) +LIBOBJS+=ppc-dis.o +endif +ifeq ($(findstring mips, $(TARGET_ARCH) $(ARCH)),mips) +LIBOBJS+=mips-dis.o +endif +ifeq ($(findstring sparc, $(TARGET_BASE_ARCH) $(ARCH)),sparc) +LIBOBJS+=sparc-dis.o +endif +ifeq ($(findstring arm, $(TARGET_ARCH) $(ARCH)),arm) +LIBOBJS+=arm-dis.o +endif +ifeq ($(findstring m68k, $(TARGET_ARCH) $(ARCH)),m68k) +LIBOBJS+=m68k-dis.o +endif +ifeq ($(findstring sh4, $(TARGET_ARCH) $(ARCH)),sh4) +LIBOBJS+=sh4-dis.o +endif + +ifdef CONFIG_GDBSTUB +OBJS+=gdbstub.o +endif + +# qemu-dm objects +LIBOBJS=helper2.o exec-dm.o i8259-dm.o + +all: $(PROGS) + +$(QEMU_USER): $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) +ifeq ($(ARCH),alpha) +# Mark as 32 bit binary, i. e. it will be mapped into the low 31 bit of +# the address space (31 bit so sign extending doesn't matter) + echo -ne '\001\000\000\000' | dd of=qemu bs=1 seek=48 count=4 conv=notrunc +endif + +# must use static linking to avoid leaving stuff in virtual address space +VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o +VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o +ifdef CONFIG_WIN32 +VL_OBJS+=tap-win32.o +endif + +SOUND_HW = sb16.o es1370.o +AUDIODRV = audio.o noaudio.o wavaudio.o +ifdef CONFIG_SDL +AUDIODRV += sdlaudio.o +endif +ifdef CONFIG_OSS +AUDIODRV += ossaudio.o +endif +ifdef CONFIG_COREAUDIO +AUDIODRV += coreaudio.o +endif +ifdef CONFIG_ALSA +AUDIODRV += alsaaudio.o +LIBS += -lasound +endif +ifdef CONFIG_DSOUND +AUDIODRV += dsoundaudio.o +LIBS += -lole32 -ldxguid +endif +ifdef CONFIG_FMOD +AUDIODRV += fmodaudio.o +audio.o fmodaudio.o: DEFINES := -I$(CONFIG_FMOD_INC) $(DEFINES) +LIBS += $(CONFIG_FMOD_LIB) +endif +ifdef CONFIG_ADLIB +SOUND_HW += fmopl.o adlib.o +endif + +# USB layer +VL_OBJS+= usb.o usb-hub.o usb-uhci.o usb-linux.o usb-hid.o + +# PCI network cards +VL_OBJS+= ne2000.o rtl8139.o + +ifeq ($(TARGET_BASE_ARCH), i386) +# Hardware support +VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) +VL_OBJS+= fdc.o mc146818rtc.o serial.o pc.o +VL_OBJS+= cirrus_vga.o mixeng.o parallel.o +VL_OBJS+= piix4acpi.o +DEFINES += -DHAS_AUDIO +endif +ifeq ($(TARGET_BASE_ARCH), ppc) +VL_OBJS+= ppc.o ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) +VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o +VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o +DEFINES += -DHAS_AUDIO +endif +ifeq ($(TARGET_ARCH), mips) +VL_OBJS+= mips_r4k.o dma.o vga.o serial.o i8254.o i8259.o +#VL_OBJS+= #ide.o pckbd.o fdc.o m48t59.o +endif +ifeq ($(TARGET_BASE_ARCH), sparc) +ifeq ($(TARGET_ARCH), sparc64) +VL_OBJS+= sun4u.o ide.o pckbd.o ps2.o vga.o +VL_OBJS+= fdc.o mc146818rtc.o serial.o m48t59.o +VL_OBJS+= cirrus_vga.o parallel.o +else +VL_OBJS+= sun4m.o tcx.o lance.o iommu.o m48t59.o slavio_intctl.o +VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o +endif +endif +ifeq ($(TARGET_BASE_ARCH), arm) +VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o +VL_OBJS+= arm_boot.o pl011.o pl050.o pl080.o pl110.o pl190.o +endif +ifeq ($(TARGET_BASE_ARCH), sh4) +VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o +endif +ifdef CONFIG_GDBSTUB +VL_OBJS+=gdbstub.o +endif +ifdef CONFIG_SDL +VL_OBJS+=sdl.o +endif +VL_OBJS+=vnc.o +ifdef CONFIG_COCOA +VL_OBJS+=cocoa.o +COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit +ifdef CONFIG_COREAUDIO +COCOA_LIBS+=-framework CoreAudio +endif +endif +ifdef CONFIG_SLIRP +DEFINES+=-I$(SRC_PATH)/slirp +SLIRP_OBJS=cksum.o if.o ip_icmp.o ip_input.o ip_output.o \ +slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o \ +tcp_subr.o tcp_timer.o udp.o bootp.o debug.o tftp.o +VL_OBJS+=$(addprefix slirp/, $(SLIRP_OBJS)) +endif + +VL_LDFLAGS= +# specific flags are needed for non soft mmu emulator +ifdef CONFIG_STATIC +VL_LDFLAGS+=-static +endif +ifndef CONFIG_SOFTMMU +VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386-vl.ld +endif +ifndef CONFIG_DARWIN +ifndef CONFIG_WIN32 +ifndef CONFIG_SOLARIS +VL_LIBS=-lutil +endif +endif +endif +ifdef TARGET_GPROF +vl.o: CFLAGS+=-p +VL_LDFLAGS+=-p +endif + +ifeq ($(ARCH),ia64) +VL_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld +endif + +ifdef CONFIG_WIN32 +SDL_LIBS := $(filter-out -mwindows, $(SDL_LIBS)) -mconsole +endif + +$(QEMU_SYSTEM): $(VL_OBJS) libqemu.a + $(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS) + +cocoa.o: cocoa.m + $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + +sdl.o: sdl.c keymaps.c sdl_keysym.h + $(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $< + +vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h + $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + +sdlaudio.o: sdlaudio.c + $(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $< + +depend: $(SRCS) + $(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend + +vldepend: $(VL_OBJS:.o=.c) + $(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend + +# libqemu + +libqemu.a: $(LIBOBJS) + rm -f $@ + $(AR) rcs $@ $(LIBOBJS) + +translate.o: translate.c gen-op.h opc.h cpu.h + +translate-all.o: translate-all.c opc.h cpu.h + +translate-op.o: translate-all.c op.h opc.h cpu.h + +op.h: op.o $(DYNGEN) + $(DYNGEN) -o $@ $< + +opc.h: op.o $(DYNGEN) + $(DYNGEN) -c -o $@ $< + +gen-op.h: op.o $(DYNGEN) + $(DYNGEN) -g -o $@ $< + +op.o: op.c + $(CC) $(OP_CFLAGS) $(DEFINES) -c -o $@ $< + +helper.o: helper.c + $(CC) $(HELPER_CFLAGS) $(DEFINES) -c -o $@ $< + +ifeq ($(TARGET_BASE_ARCH), i386) +op.o: op.c opreg_template.h ops_template.h ops_template_mem.h ops_mem.h ops_sse.h +endif + +ifeq ($(TARGET_ARCH), arm) +op.o: op.c op_template.h +pl110.o: pl110_template.h +endif + +ifeq ($(TARGET_BASE_ARCH), sparc) +op.o: op.c op_template.h op_mem.h fop_template.h fbranch_template.h +magic_load.o: elf_op.h +endif + +ifeq ($(TARGET_BASE_ARCH), ppc) +op.o: op.c op_template.h op_mem.h +op_helper.o: op_helper_mem.h +translate.o: translate.c translate_init.c +endif + +ifeq ($(TARGET_ARCH), mips) +op.o: op.c op_template.c op_mem.c +op_helper.o: op_helper_mem.c +endif + +loader.o: loader.c elf_ops.h + +ifeq ($(TARGET_ARCH), sh4) +op.o: op.c op_mem.c cpu.h +op_helper.o: op_helper.c exec.h cpu.h +helper.o: helper.c exec.h cpu.h +sh7750.o: sh7750.c sh7750_regs.h sh7750_regnames.h cpu.h +shix.o: shix.c sh7750_regs.h sh7750_regnames.h +sh7750_regnames.o: sh7750_regnames.c sh7750_regnames.h sh7750_regs.h +tc58128.o: tc58128.c +endif + +%.o: %.c + $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + +%.o: %.S + $(CC) $(DEFINES) -c -o $@ $< + +clean: + rm -f *.o *.a *~ $(PROGS) gen-op.h opc.h op.h nwfpe/*.o slirp/*.o fpu/*.o + +distclean: clean + rm -rf config.mak config.h + +install: all + mkdir -p "$(DESTDIR)$(bindir)" "$(DESTDIR)$(configdir)" +ifneq ($(PROGS),) + $(INSTALL) -m 755 -s $(PROGS) "$(DESTDIR)$(bindir)" +endif + install -m 755 $(TARGET_PATH)/qemu-dm.debug "$(DESTDIR)$(bindir)" + install -m 755 $(TARGET_PATH)/qemu-ifup "$(DESTDIR)$(configdir)" + +ifneq ($(wildcard .depend),) +include .depend +endif + +ifeq (1, 0) +audio.o sdlaudio.o dsoundaudio.o ossaudio.o wavaudio.o noaudio.o \ +fmodaudio.o alsaaudio.o mixeng.o sb16.o es1370.o gus.o adlib.o: \ +CFLAGS := $(CFLAGS) -Wall -Werror -W -Wsign-compare +endif diff --git a/tools/ioemu/README b/tools/ioemu/README new file mode 100644 index 0000000000..1a39500b78 --- /dev/null +++ b/tools/ioemu/README @@ -0,0 +1,3 @@ +Read the documentation in qemu-doc.html. + +Fabrice Bellard. \ No newline at end of file diff --git a/tools/ioemu/README.distrib b/tools/ioemu/README.distrib new file mode 100644 index 0000000000..a1598a2998 --- /dev/null +++ b/tools/ioemu/README.distrib @@ -0,0 +1,16 @@ +Information about the various packages used to build the current qemu +x86 binary distribution: + +* gcc 2.95.2 was used for the build. A glibc 2.1.3 Debian distribution + was used to get most of the binary packages. + +* wine-20020411 tarball + + ./configure --prefix=/usr/local/wine-i386 + + All exe and libs were stripped. Some compile time tools and the + includes were deleted. + +* ldconfig was launched to build the library links: + + qemu-i386 /usr/gnemul/qemu-i386/bin/ldconfig-i386 -C /usr/gnemul/qemu-i386/etc/ld.so.cache diff --git a/tools/ioemu/TODO b/tools/ioemu/TODO new file mode 100644 index 0000000000..8cc9aa55f0 --- /dev/null +++ b/tools/ioemu/TODO @@ -0,0 +1,61 @@ +short term: +---------- +- support variable tsc freq +- cpu_interrupt() win32/SMP fix +- USB host async +- IDE async +- debug option in 'configure' script + disable -fomit-frame-pointer +- Precise VGA timings for old games/demos (malc patch) +- merge PIC spurious interrupt patch +- merge Solaris patch +- warning for OS/2: must not use 128 MB memory (merge bochs cmos patch ?) +- config file (at least for windows/Mac OS X) +- commit message if execution of code in IO memory +- update doc: PCI infos. +- VNC patch + Synaptic patch. +- basic VGA optimizations +- physical memory cache (reduce qemu-fast address space size to about 32 MB) +- better code fetch (different exception handling + CS.limit support) +- do not resize vga if invalid size. +- avoid looping if only exceptions +- cycle counter for all archs +- TLB code protection support for PPC +- see openMosix Doc +- disable SMC handling for ARM/SPARC/PPC (not finished) +- see undefined flags for BTx insn +- user/kernel PUSHL/POPL in helper.c +- keyboard output buffer filling timing emulation +- return UD exception if LOCK prefix incorrectly used +- test ldt limit < 7 ? +- tests for each target CPU +- fix CCOP optimisation +- fix all remaining thread lock issues (must put TBs in a specific invalid + state, find a solution for tb_flush()). +- fix arm fpu rounding (at least for float->integer conversions) + +ppc specific: +------------ +- TLB invalidate not needed if msr_pr changes +- SPR_ENCODE() not useful +- enable shift optimizations ? + +linux-user specific: +------------------- +- add IPC syscalls +- handle rare page fault cases (in particular if page fault in helpers or + in syscall emulation code). +- more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit + issues, fix 16 bit uid issues) +- use page_unprotect_range in every suitable syscall to handle all + cases of self modifying code. +- fix thread stack freeing (use kernel 2.5.x CLONE_CHILD_CLEARTID) +- use kernel traps for unaligned accesses on ARM ? + + +lower priority: +-------------- +- int15 ah=86: use better timing +- suppress shift_mem ops +- fix some 16 bit sp push/pop overflow (pusha/popa, lcall lret) +- optimize FPU operations (evaluate x87 stack pointer statically) +- use -msoft-float on ARM diff --git a/tools/ioemu/VERSION b/tools/ioemu/VERSION new file mode 100644 index 0000000000..c18d72be30 --- /dev/null +++ b/tools/ioemu/VERSION @@ -0,0 +1 @@ +0.8.1 \ No newline at end of file diff --git a/tools/ioemu/a.out.h b/tools/ioemu/a.out.h new file mode 100644 index 0000000000..1f978c1c04 --- /dev/null +++ b/tools/ioemu/a.out.h @@ -0,0 +1,431 @@ +/* a.out.h + + Copyright 1997, 1998, 1999, 2001 Red Hat, Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#ifndef _A_OUT_H_ +#define _A_OUT_H_ + +#ifdef __cplusplus +extern "C" { +#endif +#define COFF_IMAGE_WITH_PE +#define COFF_LONG_SECTION_NAMES + +/*** coff information for Intel 386/486. */ + + +/********************** FILE HEADER **********************/ + +struct external_filehdr { + short f_magic; /* magic number */ + short f_nscns; /* number of sections */ + unsigned long f_timdat; /* time & date stamp */ + unsigned long f_symptr; /* file pointer to symtab */ + unsigned long f_nsyms; /* number of symtab entries */ + short f_opthdr; /* sizeof(optional hdr) */ + short f_flags; /* flags */ +}; + +/* Bits for f_flags: + * F_RELFLG relocation info stripped from file + * F_EXEC file is executable (no unresolved external references) + * F_LNNO line numbers stripped from file + * F_LSYMS local symbols stripped from file + * F_AR32WR file has byte ordering of an AR32WR machine (e.g. vax) + */ + +#define F_RELFLG (0x0001) +#define F_EXEC (0x0002) +#define F_LNNO (0x0004) +#define F_LSYMS (0x0008) + + + +#define I386MAGIC 0x14c +#define I386PTXMAGIC 0x154 +#define I386AIXMAGIC 0x175 + +/* This is Lynx's all-platform magic number for executables. */ + +#define LYNXCOFFMAGIC 0415 + +#define I386BADMAG(x) (((x).f_magic != I386MAGIC) \ + && (x).f_magic != I386AIXMAGIC \ + && (x).f_magic != I386PTXMAGIC \ + && (x).f_magic != LYNXCOFFMAGIC) + +#define FILHDR struct external_filehdr +#define FILHSZ 20 + + +/********************** AOUT "OPTIONAL HEADER"= + **********************/ + + +typedef struct +{ + unsigned short magic; /* type of file */ + unsigned short vstamp; /* version stamp */ + unsigned long tsize; /* text size in bytes, padded to FW bdry*/ + unsigned long dsize; /* initialized data " " */ + unsigned long bsize; /* uninitialized data " " */ + unsigned long entry; /* entry pt. */ + unsigned long text_start; /* base of text used for this file */ + unsigned long data_start; /* base of data used for this file= + */ +} +AOUTHDR; + +#define AOUTSZ 28 +#define AOUTHDRSZ 28 + +#define OMAGIC 0404 /* object files, eg as output */ +#define ZMAGIC 0413 /* demand load format, eg normal ld output */ +#define STMAGIC 0401 /* target shlib */ +#define SHMAGIC 0443 /* host shlib */ + + +/* define some NT default values */ +/* #define NT_IMAGE_BASE 0x400000 moved to internal.h */ +#define NT_SECTION_ALIGNMENT 0x1000 +#define NT_FILE_ALIGNMENT 0x200 +#define NT_DEF_RESERVE 0x100000 +#define NT_DEF_COMMIT 0x1000 + +/********************** SECTION HEADER **********************/ + + +struct external_scnhdr { + char s_name[8]; /* section name */ + unsigned long s_paddr; /* physical address, offset + of last addr in scn */ + unsigned long s_vaddr; /* virtual address */ + unsigned long s_size; /* section size */ + unsigned long s_scnptr; /* file ptr to raw data for section */ + unsigned long s_relptr; /* file ptr to relocation */ + unsigned long s_lnnoptr; /* file ptr to line numbers */ + unsigned short s_nreloc; /* number of relocation entries */ + unsigned short s_nlnno; /* number of line number entries*/ + unsigned long s_flags; /* flags */ +}; + +#define SCNHDR struct external_scnhdr +#define SCNHSZ 40 + +/* + * names of "special" sections + */ +#define _TEXT ".text" +#define _DATA ".data" +#define _BSS ".bss" +#define _COMMENT ".comment" +#define _LIB ".lib" + +/********************** LINE NUMBERS **********************/ + +/* 1 line number entry for every "breakpointable" source line in a section. + * Line numbers are grouped on a per function basis; first entry in a function + * grouping will have l_lnno = 0 and in place of physical address will be the + * symbol table index of the function name. + */ +struct external_lineno { + union { + unsigned long l_symndx; /* function name symbol index, iff l_lnno 0 */ + unsigned long l_paddr; /* (physical) address of line number */ + } l_addr; + unsigned short l_lnno; /* line number */ +}; + +#define LINENO struct external_lineno +#define LINESZ 6 + +/********************** SYMBOLS **********************/ + +#define E_SYMNMLEN 8 /* # characters in a symbol name */ +#define E_FILNMLEN 14 /* # characters in a file name */ +#define E_DIMNUM 4 /* # array dimensions in auxiliary entry */ + +struct __attribute__((packed)) external_syment +{ + union { + char e_name[E_SYMNMLEN]; + struct { + unsigned long e_zeroes; + unsigned long e_offset; + } e; + } e; + unsigned long e_value; + unsigned short e_scnum; + unsigned short e_type; + char e_sclass[1]; + char e_numaux[1]; +}; + +#define N_BTMASK (0xf) +#define N_TMASK (0x30) +#define N_BTSHFT (4) +#define N_TSHIFT (2) + +union external_auxent { + struct { + unsigned long x_tagndx; /* str, un, or enum tag indx */ + union { + struct { + unsigned short x_lnno; /* declaration line number */ + unsigned short x_size; /* str/union/array size */ + } x_lnsz; + unsigned long x_fsize; /* size of function */ + } x_misc; + union { + struct { /* if ISFCN, tag, or .bb */ + unsigned long x_lnnoptr;/* ptr to fcn line # */ + unsigned long x_endndx; /* entry ndx past block end */ + } x_fcn; + struct { /* if ISARY, up to 4 dimen. */ + char x_dimen[E_DIMNUM][2]; + } x_ary; + } x_fcnary; + unsigned short x_tvndx; /* tv index */ + } x_sym; + + union { + char x_fname[E_FILNMLEN]; + struct { + unsigned long x_zeroes; + unsigned long x_offset; + } x_n; + } x_file; + + struct { + unsigned long x_scnlen; /* section length */ + unsigned short x_nreloc; /* # relocation entries */ + unsigned short x_nlinno; /* # line numbers */ + unsigned long x_checksum; /* section COMDAT checksum */ + unsigned short x_associated;/* COMDAT associated section index */ + char x_comdat[1]; /* COMDAT selection number */ + } x_scn; + + struct { + unsigned long x_tvfill; /* tv fill value */ + unsigned short x_tvlen; /* length of .tv */ + char x_tvran[2][2]; /* tv range */ + } x_tv; /* info about .tv section (in auxent of symbol .tv)) */ + +}; + +#define SYMENT struct external_syment +#define SYMESZ 18 +#define AUXENT union external_auxent +#define AUXESZ 18 + +#define _ETEXT "etext" + +/********************** RELOCATION DIRECTIVES **********************/ + +struct external_reloc { + char r_vaddr[4]; + char r_symndx[4]; + char r_type[2]; +}; + +#define RELOC struct external_reloc +#define RELSZ 10 + +/* end of coff/i386.h */ + +/* PE COFF header information */ + +#ifndef _PE_H +#define _PE_H + +/* NT specific file attributes */ +#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 +#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 +#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 +#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 +#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 +#define IMAGE_FILE_32BIT_MACHINE 0x0100 +#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 +#define IMAGE_FILE_SYSTEM 0x1000 +#define IMAGE_FILE_DLL 0x2000 +#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 + +/* additional flags to be set for section headers to allow the NT loader to + read and write to the section data (to replace the addresses of data in + dlls for one thing); also to execute the section in .text's case= + */ +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_WRITE 0x80000000 + +/* + * Section characteristics added for ppc-nt + */ + +#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* Reserved. */ + +#define IMAGE_SCN_CNT_CODE 0x00000020 /* Section contains code. */ +#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* Section contains initialized data. */ +#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* Section contains uninitialized data. */ + +#define IMAGE_SCN_LNK_OTHER 0x00000100 /* Reserved. */ +#define IMAGE_SCN_LNK_INFO 0x00000200 /* Section contains comments or some other type of information. */ +#define IMAGE_SCN_LNK_REMOVE 0x00000800 /* Section contents will not become part of image. */ +#define IMAGE_SCN_LNK_COMDAT 0x00001000 /* Section contents comdat. */ + +#define IMAGE_SCN_MEM_FARDATA 0x00008000 + +#define IMAGE_SCN_MEM_PURGEABLE 0x00020000 +#define IMAGE_SCN_MEM_16BIT 0x00020000 +#define IMAGE_SCN_MEM_LOCKED 0x00040000 +#define IMAGE_SCN_MEM_PRELOAD 0x00080000 + +#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 +#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 +#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 +#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 +#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 /* Default alignment if no others are specified. */ +#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 +#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 + + +#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* Section contains extended relocations. */ +#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* Section is not cachable. */ +#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 /* Section is not pageable. */ +#define IMAGE_SCN_MEM_SHARED 0x10000000 /* Section is shareable. */ + +/* COMDAT selection codes. */ + +#define IMAGE_COMDAT_SELECT_NODUPLICATES (1) /* Warn if duplicates. */ +#define IMAGE_COMDAT_SELECT_ANY (2) /* No warning. */ +#define IMAGE_COMDAT_SELECT_SAME_SIZE (3) /* Warn if different size. */ +#define IMAGE_COMDAT_SELECT_EXACT_MATCH (4) /* Warn if different. */ +#define IMAGE_COMDAT_SELECT_ASSOCIATIVE (5) /* Base on other section. */ + +/* Magic values that are true for all dos/nt implementations */ +#define DOSMAGIC 0x5a4d +#define NT_SIGNATURE 0x00004550 + +/* NT allows long filenames, we want to accommodate this. This may break + some of the bfd functions */ +#undef FILNMLEN +#define FILNMLEN 18 /* # characters in a file name */ + + +#ifdef COFF_IMAGE_WITH_PE +/* The filehdr is only weired in images */ + +#undef FILHDR +struct external_PE_filehdr +{ + /* DOS header fields */ + unsigned short e_magic; /* Magic number, 0x5a4d */ + unsigned short e_cblp; /* Bytes on last page of file, 0x90 */ + unsigned short e_cp; /* Pages in file, 0x3 */ + unsigned short e_crlc; /* Relocations, 0x0 */ + unsigned short e_cparhdr; /* Size of header in paragraphs, 0x4 */ + unsigned short e_minalloc; /* Minimum extra paragraphs needed, 0x0 */ + unsigned short e_maxalloc; /* Maximum extra paragraphs needed, 0xFFFF */ + unsigned short e_ss; /* Initial (relative) SS value, 0x0 */ + unsigned short e_sp; /* Initial SP value, 0xb8 */ + unsigned short e_csum; /* Checksum, 0x0 */ + unsigned short e_ip; /* Initial IP value, 0x0 */ + unsigned short e_cs; /* Initial (relative) CS value, 0x0 */ + unsigned short e_lfarlc; /* File address of relocation table, 0x40 */ + unsigned short e_ovno; /* Overlay number, 0x0 */ + char e_res[4][2]; /* Reserved words, all 0x0 */ + unsigned short e_oemid; /* OEM identifier (for e_oeminfo), 0x0 */ + unsigned short e_oeminfo; /* OEM information; e_oemid specific, 0x0 */ + char e_res2[10][2]; /* Reserved words, all 0x0 */ + unsigned long e_lfanew; /* File address of new exe header, 0x80 */ + char dos_message[16][4]; /* other stuff, always follow DOS header */ + unsigned int nt_signature; /* required NT signature, 0x4550 */ + + /* From standard header */ + + unsigned short f_magic; /* magic number */ + unsigned short f_nscns; /* number of sections */ + unsigned long f_timdat; /* time & date stamp */ + unsigned long f_symptr; /* file pointer to symtab */ + unsigned long f_nsyms; /* number of symtab entries */ + unsigned short f_opthdr; /* sizeof(optional hdr) */ + unsigned short f_flags; /* flags */ +}; + + +#define FILHDR struct external_PE_filehdr +#undef FILHSZ +#define FILHSZ 152 + +#endif + +typedef struct +{ + unsigned short magic; /* type of file */ + unsigned short vstamp; /* version stamp */ + unsigned long tsize; /* text size in bytes, padded to FW bdry*/ + unsigned long dsize; /* initialized data " " */ + unsigned long bsize; /* uninitialized data " " */ + unsigned long entry; /* entry pt. */ + unsigned long text_start; /* base of text used for this file */ + unsigned long data_start; /* base of all data used for this file */ + + /* NT extra fields; see internal.h for descriptions */ + unsigned long ImageBase; + unsigned long SectionAlignment; + unsigned long FileAlignment; + unsigned short MajorOperatingSystemVersion; + unsigned short MinorOperatingSystemVersion; + unsigned short MajorImageVersion; + unsigned short MinorImageVersion; + unsigned short MajorSubsystemVersion; + unsigned short MinorSubsystemVersion; + char Reserved1[4]; + unsigned long SizeOfImage; + unsigned long SizeOfHeaders; + unsigned long CheckSum; + unsigned short Subsystem; + unsigned short DllCharacteristics; + unsigned long SizeOfStackReserve; + unsigned long SizeOfStackCommit; + unsigned long SizeOfHeapReserve; + unsigned long SizeOfHeapCommit; + unsigned long LoaderFlags; + unsigned long NumberOfRvaAndSizes; + /* IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; */ + char DataDirectory[16][2][4]; /* 16 entries, 2 elements/entry, 4 chars */ + +} PEAOUTHDR; + + +#undef AOUTSZ +#define AOUTSZ (AOUTHDRSZ + 196) + +#undef E_FILNMLEN +#define E_FILNMLEN 18 /* # characters in a file name */ +#endif + +/* end of coff/pe.h */ + +#define DT_NON (0) /* no derived type */ +#define DT_PTR (1) /* pointer */ +#define DT_FCN (2) /* function */ +#define DT_ARY (3) /* array */ + +#define ISPTR(x) (((x) & N_TMASK) == (DT_PTR << N_BTSHFT)) +#define ISFCN(x) (((x) & N_TMASK) == (DT_FCN << N_BTSHFT)) +#define ISARY(x) (((x) & N_TMASK) == (DT_ARY << N_BTSHFT)) + +#ifdef __cplusplus +} +#endif + +#endif /* _A_OUT_H_ */ + diff --git a/tools/ioemu/aes.c b/tools/ioemu/aes.c new file mode 100644 index 0000000000..cd4484ff9b --- /dev/null +++ b/tools/ioemu/aes.c @@ -0,0 +1,1317 @@ +/** + * + * aes.c - integrated in QEMU by Fabrice Bellard from the OpenSSL project. + */ +/* + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "vl.h" +#include "aes.h" + +#define NDEBUG +#include + +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +#define MAXKC (256/32) +#define MAXKB (256/8) +#define MAXNR 14 + +/* This controls loop-unrolling in aes_core.c */ +#undef FULL_UNROLL +# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +# define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +static const u32 Te0[256] = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +static const u32 Te1[256] = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +static const u32 Te2[256] = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +static const u32 Te3[256] = { + + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; +static const u32 Te4[256] = { + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; +static const u32 Td0[256] = { + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; +static const u32 Td1[256] = { + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; +static const u32 Td2[256] = { + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; +static const u32 Td3[256] = { + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; +static const u32 Td4[256] = { + 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, + 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, + 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, + 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, + 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, + 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, + 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, + 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, + 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, + 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, + 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, + 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, + 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, + 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, + 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, + 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, + 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, + 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, + 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, + 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, + 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, + 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, + 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, + 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, + 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, + 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, + 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, + 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, + 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, + 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, + 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, + 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, + 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, + 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, + 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, + 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, + 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, + 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, + 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, + 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, + 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, + 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, + 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, + 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, + 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, + 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, + 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, + 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, + 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, + 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, + 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, + 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, + 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, + 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, + 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, + 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, + 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, + 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, + 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, + 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, + 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, + 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, + 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, + 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, +}; +static const u32 rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +/** + * Expand the cipher key into the encryption key schedule. + */ +int AES_set_encrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key) { + + u32 *rk; + int i = 0; + u32 temp; + + if (!userKey || !key) + return -1; + if (bits != 128 && bits != 192 && bits != 256) + return -2; + + rk = key->rd_key; + + if (bits==128) + key->rounds = 10; + else if (bits==192) + key->rounds = 12; + else + key->rounds = 14; + + rk[0] = GETU32(userKey ); + rk[1] = GETU32(userKey + 4); + rk[2] = GETU32(userKey + 8); + rk[3] = GETU32(userKey + 12); + if (bits == 128) { + while (1) { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) { + return 0; + } + rk += 4; + } + } + rk[4] = GETU32(userKey + 16); + rk[5] = GETU32(userKey + 20); + if (bits == 192) { + while (1) { + temp = rk[ 5]; + rk[ 6] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) { + return 0; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32(userKey + 24); + rk[7] = GETU32(userKey + 28); + if (bits == 256) { + while (1) { + temp = rk[ 7]; + rk[ 8] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) { + return 0; + } + temp = rk[11]; + rk[12] = rk[ 4] ^ + (Te4[(temp >> 24) ] & 0xff000000) ^ + (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(temp ) & 0xff] & 0x000000ff); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + + rk += 8; + } + } + return 0; +} + +/** + * Expand the cipher key into the decryption key schedule. + */ +int AES_set_decrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key) { + + u32 *rk; + int i, j, status; + u32 temp; + + /* first, start with an encryption schedule */ + status = AES_set_encrypt_key(userKey, bits, key); + if (status < 0) + return status; + + rk = key->rd_key; + + /* invert the order of the round keys: */ + for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for (i = 1; i < (key->rounds); i++) { + rk += 4; + rk[0] = + Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[0] ) & 0xff] & 0xff]; + rk[1] = + Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[1] ) & 0xff] & 0xff]; + rk[2] = + Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[2] ) & 0xff] & 0xff]; + rk[3] = + Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[3] ) & 0xff] & 0xff]; + } + return 0; +} + +#ifndef AES_ASM +/* + * Encrypt a single block + * in and out can overlap + */ +void AES_encrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key) { + + const u32 *rk; + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + assert(in && out && key); + rk = key->rd_key; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(in ) ^ rk[0]; + s1 = GETU32(in + 4) ^ rk[1]; + s2 = GETU32(in + 8) ^ rk[2]; + s3 = GETU32(in + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + if (key->rounds > 10) { + /* round 10: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47]; + if (key->rounds > 12) { + /* round 12: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; + } + } + rk += key->rounds << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = key->rounds >> 1; + for (;;) { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24) ] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(out , s0); + s1 = + (Te4[(t1 >> 24) ] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(out + 4, s1); + s2 = + (Te4[(t2 >> 24) ] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(out + 8, s2); + s3 = + (Te4[(t3 >> 24) ] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(out + 12, s3); +} + +/* + * Decrypt a single block + * in and out can overlap + */ +void AES_decrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key) { + + const u32 *rk; + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + assert(in && out && key); + rk = key->rd_key; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(in ) ^ rk[0]; + s1 = GETU32(in + 4) ^ rk[1]; + s2 = GETU32(in + 8) ^ rk[2]; + s3 = GETU32(in + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + if (key->rounds > 10) { + /* round 10: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47]; + if (key->rounds > 12) { + /* round 12: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55]; + } + } + rk += key->rounds << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = key->rounds >> 1; + for (;;) { + t0 = + Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Td0[(t0 >> 24) ] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1 ) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24) ] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2 ) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24) ] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3 ) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24) ] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24) ] & 0xff000000) ^ + (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(out , s0); + s1 = + (Td4[(t1 >> 24) ] & 0xff000000) ^ + (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(out + 4, s1); + s2 = + (Td4[(t2 >> 24) ] & 0xff000000) ^ + (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(out + 8, s2); + s3 = + (Td4[(t3 >> 24) ] & 0xff000000) ^ + (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(out + 12, s3); +} + +#endif /* AES_ASM */ + +void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, + const unsigned long length, const AES_KEY *key, + unsigned char *ivec, const int enc) +{ + + unsigned long n; + unsigned long len = length; + unsigned char tmp[AES_BLOCK_SIZE]; + + assert(in && out && key && ivec); + + if (enc) { + while (len >= AES_BLOCK_SIZE) { + for(n=0; n < AES_BLOCK_SIZE; ++n) + tmp[n] = in[n] ^ ivec[n]; + AES_encrypt(tmp, out, key); + memcpy(ivec, out, AES_BLOCK_SIZE); + len -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } + if (len) { + for(n=0; n < len; ++n) + tmp[n] = in[n] ^ ivec[n]; + for(n=len; n < AES_BLOCK_SIZE; ++n) + tmp[n] = ivec[n]; + AES_encrypt(tmp, tmp, key); + memcpy(out, tmp, AES_BLOCK_SIZE); + memcpy(ivec, tmp, AES_BLOCK_SIZE); + } + } else { + while (len >= AES_BLOCK_SIZE) { + memcpy(tmp, in, AES_BLOCK_SIZE); + AES_decrypt(in, out, key); + for(n=0; n < AES_BLOCK_SIZE; ++n) + out[n] ^= ivec[n]; + memcpy(ivec, tmp, AES_BLOCK_SIZE); + len -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } + if (len) { + memcpy(tmp, in, AES_BLOCK_SIZE); + AES_decrypt(tmp, tmp, key); + for(n=0; n < len; ++n) + out[n] = tmp[n] ^ ivec[n]; + memcpy(ivec, tmp, AES_BLOCK_SIZE); + } + } +} diff --git a/tools/ioemu/aes.h b/tools/ioemu/aes.h new file mode 100644 index 0000000000..a0167eb7d5 --- /dev/null +++ b/tools/ioemu/aes.h @@ -0,0 +1,26 @@ +#ifndef QEMU_AES_H +#define QEMU_AES_H + +#define AES_MAXNR 14 +#define AES_BLOCK_SIZE 16 + +struct aes_key_st { + uint32_t rd_key[4 *(AES_MAXNR + 1)]; + int rounds; +}; +typedef struct aes_key_st AES_KEY; + +int AES_set_encrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key); +int AES_set_decrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key); + +void AES_encrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key); +void AES_decrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key); +void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, + const unsigned long length, const AES_KEY *key, + unsigned char *ivec, const int enc); + +#endif diff --git a/tools/ioemu/audio/CVS/Entries b/tools/ioemu/audio/CVS/Entries new file mode 100644 index 0000000000..bc177d5db8 --- /dev/null +++ b/tools/ioemu/audio/CVS/Entries @@ -0,0 +1,19 @@ +/alsaaudio.c/1.5/Sun Nov 20 18:53:05 2005//Trelease_0_8_1 +/audio.c/1.9/Thu May 25 18:22:31 2006//Trelease_0_8_1 +/audio.h/1.5/Thu May 25 18:22:31 2006//Trelease_0_8_1 +/audio_int.h/1.7/Thu May 25 18:22:31 2006//Trelease_0_8_1 +/audio_template.h/1.4/Sun Nov 20 16:24:34 2005//Trelease_0_8_1 +/coreaudio.c/1.5/Sun Nov 20 18:53:42 2005//Trelease_0_8_1 +/dsound_template.h/1.2/Sat Nov 5 18:55:27 2005//Trelease_0_8_1 +/dsoundaudio.c/1.2/Sat Nov 5 18:55:27 2005//Trelease_0_8_1 +/fmodaudio.c/1.5/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/mixeng.c/1.4/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/mixeng.h/1.2/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/mixeng_template.h/1.2/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/noaudio.c/1.4/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/ossaudio.c/1.9/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/rate_template.h/1.2/Sat Nov 5 18:55:27 2005//Trelease_0_8_1 +/sdlaudio.c/1.6/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/sys-queue.h/1.1/Sun Oct 30 18:58:22 2005//Trelease_0_8_1 +/wavaudio.c/1.6/Thu May 25 18:22:32 2006//Trelease_0_8_1 +D diff --git a/tools/ioemu/audio/CVS/Repository b/tools/ioemu/audio/CVS/Repository new file mode 100644 index 0000000000..582b845b1e --- /dev/null +++ b/tools/ioemu/audio/CVS/Repository @@ -0,0 +1 @@ +qemu/audio diff --git a/tools/ioemu/audio/CVS/Root b/tools/ioemu/audio/CVS/Root new file mode 100644 index 0000000000..e8a9815e6a --- /dev/null +++ b/tools/ioemu/audio/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.savannah.nongnu.org:/sources/qemu diff --git a/tools/ioemu/audio/CVS/Tag b/tools/ioemu/audio/CVS/Tag new file mode 100644 index 0000000000..31c251a4bf --- /dev/null +++ b/tools/ioemu/audio/CVS/Tag @@ -0,0 +1 @@ +Nrelease_0_8_1 diff --git a/tools/ioemu/audio/alsaaudio.c b/tools/ioemu/audio/alsaaudio.c new file mode 100644 index 0000000000..30f1e5076f --- /dev/null +++ b/tools/ioemu/audio/alsaaudio.c @@ -0,0 +1,981 @@ +/* + * QEMU ALSA audio driver + * + * Copyright (c) 2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include "vl.h" + +#define AUDIO_CAP "alsa" +#include "audio_int.h" + +typedef struct ALSAVoiceOut { + HWVoiceOut hw; + void *pcm_buf; + snd_pcm_t *handle; +} ALSAVoiceOut; + +typedef struct ALSAVoiceIn { + HWVoiceIn hw; + snd_pcm_t *handle; + void *pcm_buf; +} ALSAVoiceIn; + +static struct { + int size_in_usec_in; + int size_in_usec_out; + const char *pcm_name_in; + const char *pcm_name_out; + unsigned int buffer_size_in; + unsigned int period_size_in; + unsigned int buffer_size_out; + unsigned int period_size_out; + unsigned int threshold; + + int buffer_size_in_overriden; + int period_size_in_overriden; + + int buffer_size_out_overriden; + int period_size_out_overriden; + int verbose; +} conf = { +#ifdef HIGH_LATENCY + .size_in_usec_in = 1, + .size_in_usec_out = 1, +#endif + .pcm_name_out = "hw:0,0", + .pcm_name_in = "hw:0,0", +#ifdef HIGH_LATENCY + .buffer_size_in = 400000, + .period_size_in = 400000 / 4, + .buffer_size_out = 400000, + .period_size_out = 400000 / 4, +#else +#define DEFAULT_BUFFER_SIZE 1024 +#define DEFAULT_PERIOD_SIZE 256 + .buffer_size_in = DEFAULT_BUFFER_SIZE * 4, + .period_size_in = DEFAULT_PERIOD_SIZE * 4, + .buffer_size_out = DEFAULT_BUFFER_SIZE, + .period_size_out = DEFAULT_PERIOD_SIZE, + .buffer_size_in_overriden = 0, + .buffer_size_out_overriden = 0, + .period_size_in_overriden = 0, + .period_size_out_overriden = 0, +#endif + .threshold = 0, + .verbose = 0 +}; + +struct alsa_params_req { + int freq; + audfmt_e fmt; + int nchannels; + unsigned int buffer_size; + unsigned int period_size; +}; + +struct alsa_params_obt { + int freq; + audfmt_e fmt; + int nchannels; + snd_pcm_uframes_t samples; +}; + +static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); +} + +static void GCC_FMT_ATTR (3, 4) alsa_logerr2 ( + int err, + const char *typ, + const char *fmt, + ... + ) +{ + va_list ap; + + AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); +} + +static void alsa_anal_close (snd_pcm_t **handlep) +{ + int err = snd_pcm_close (*handlep); + if (err) { + alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep); + } + *handlep = NULL; +} + +static int alsa_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int aud_to_alsafmt (audfmt_e fmt) +{ + switch (fmt) { + case AUD_FMT_S8: + return SND_PCM_FORMAT_S8; + + case AUD_FMT_U8: + return SND_PCM_FORMAT_U8; + + case AUD_FMT_S16: + return SND_PCM_FORMAT_S16_LE; + + case AUD_FMT_U16: + return SND_PCM_FORMAT_U16_LE; + + default: + dolog ("Internal logic error: Bad audio format %d\n", fmt); +#ifdef DEBUG_AUDIO + abort (); +#endif + return SND_PCM_FORMAT_U8; + } +} + +static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness) +{ + switch (alsafmt) { + case SND_PCM_FORMAT_S8: + *endianness = 0; + *fmt = AUD_FMT_S8; + break; + + case SND_PCM_FORMAT_U8: + *endianness = 0; + *fmt = AUD_FMT_U8; + break; + + case SND_PCM_FORMAT_S16_LE: + *endianness = 0; + *fmt = AUD_FMT_S16; + break; + + case SND_PCM_FORMAT_U16_LE: + *endianness = 0; + *fmt = AUD_FMT_U16; + break; + + case SND_PCM_FORMAT_S16_BE: + *endianness = 1; + *fmt = AUD_FMT_S16; + break; + + case SND_PCM_FORMAT_U16_BE: + *endianness = 1; + *fmt = AUD_FMT_U16; + break; + + default: + dolog ("Unrecognized audio format %d\n", alsafmt); + return -1; + } + + return 0; +} + +#if defined DEBUG_MISMATCHES || defined DEBUG +static void alsa_dump_info (struct alsa_params_req *req, + struct alsa_params_obt *obt) +{ + dolog ("parameter | requested value | obtained value\n"); + dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); + dolog ("channels | %10d | %10d\n", + req->nchannels, obt->nchannels); + dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); + dolog ("============================================\n"); + dolog ("requested: buffer size %d period size %d\n", + req->buffer_size, req->period_size); + dolog ("obtained: samples %ld\n", obt->samples); +} +#endif + +static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) +{ + int err; + snd_pcm_sw_params_t *sw_params; + + snd_pcm_sw_params_alloca (&sw_params); + + err = snd_pcm_sw_params_current (handle, sw_params); + if (err < 0) { + dolog ("Could not fully initialize DAC\n"); + alsa_logerr (err, "Failed to get current software parameters\n"); + return; + } + + err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold); + if (err < 0) { + dolog ("Could not fully initialize DAC\n"); + alsa_logerr (err, "Failed to set software threshold to %ld\n", + threshold); + return; + } + + err = snd_pcm_sw_params (handle, sw_params); + if (err < 0) { + dolog ("Could not fully initialize DAC\n"); + alsa_logerr (err, "Failed to set software parameters\n"); + return; + } +} + +static int alsa_open (int in, struct alsa_params_req *req, + struct alsa_params_obt *obt, snd_pcm_t **handlep) +{ + snd_pcm_t *handle; + snd_pcm_hw_params_t *hw_params; + int err, freq, nchannels; + const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out; + unsigned int period_size, buffer_size; + snd_pcm_uframes_t obt_buffer_size; + const char *typ = in ? "ADC" : "DAC"; + + freq = req->freq; + period_size = req->period_size; + buffer_size = req->buffer_size; + nchannels = req->nchannels; + + snd_pcm_hw_params_alloca (&hw_params); + + err = snd_pcm_open ( + &handle, + pcm_name, + in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK + ); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name); + return -1; + } + + err = snd_pcm_hw_params_any (handle, hw_params); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n"); + goto err; + } + + err = snd_pcm_hw_params_set_access ( + handle, + hw_params, + SND_PCM_ACCESS_RW_INTERLEAVED + ); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to set access type\n"); + goto err; + } + + err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt); + goto err; + } + + err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq); + goto err; + } + + err = snd_pcm_hw_params_set_channels_near ( + handle, + hw_params, + &nchannels + ); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to set number of channels %d\n", + req->nchannels); + goto err; + } + + if (nchannels != 1 && nchannels != 2) { + alsa_logerr2 (err, typ, + "Can not handle obtained number of channels %d\n", + nchannels); + goto err; + } + + if (!((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out))) { + if (!buffer_size) { + buffer_size = DEFAULT_BUFFER_SIZE; + period_size= DEFAULT_PERIOD_SIZE; + } + } + + if (buffer_size) { + if ((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out)) { + if (period_size) { + err = snd_pcm_hw_params_set_period_time_near ( + handle, + hw_params, + &period_size, + 0 + ); + if (err < 0) { + alsa_logerr2 (err, typ, + "Failed to set period time %d\n", + req->period_size); + goto err; + } + } + + err = snd_pcm_hw_params_set_buffer_time_near ( + handle, + hw_params, + &buffer_size, + 0 + ); + + if (err < 0) { + alsa_logerr2 (err, typ, + "Failed to set buffer time %d\n", + req->buffer_size); + goto err; + } + } + else { + int dir; + snd_pcm_uframes_t minval; + + if (period_size) { + minval = period_size; + dir = 0; + + err = snd_pcm_hw_params_get_period_size_min ( + hw_params, + &minval, + &dir + ); + if (err < 0) { + alsa_logerr ( + err, + "Could not get minmal period size for %s\n", + typ + ); + } + else { + if (period_size < minval) { + if ((in && conf.period_size_in_overriden) + || (!in && conf.period_size_out_overriden)) { + dolog ("%s period size(%d) is less " + "than minmal period size(%ld)\n", + typ, + period_size, + minval); + } + period_size = minval; + } + } + + err = snd_pcm_hw_params_set_period_size ( + handle, + hw_params, + period_size, + 0 + ); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to set period size %d\n", + req->period_size); + goto err; + } + } + + minval = buffer_size; + err = snd_pcm_hw_params_get_buffer_size_min ( + hw_params, + &minval + ); + if (err < 0) { + alsa_logerr (err, "Could not get minmal buffer size for %s\n", + typ); + } + else { + if (buffer_size < minval) { + if ((in && conf.buffer_size_in_overriden) + || (!in && conf.buffer_size_out_overriden)) { + dolog ( + "%s buffer size(%d) is less " + "than minimal buffer size(%ld)\n", + typ, + buffer_size, + minval + ); + } + buffer_size = minval; + } + } + + err = snd_pcm_hw_params_set_buffer_size ( + handle, + hw_params, + buffer_size + ); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to set buffer size %d\n", + req->buffer_size); + goto err; + } + } + } + else { + dolog ("warning: Buffer size is not set\n"); + } + + err = snd_pcm_hw_params (handle, hw_params); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to apply audio parameters\n"); + goto err; + } + + err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size); + if (err < 0) { + alsa_logerr2 (err, typ, "Failed to get buffer size\n"); + goto err; + } + + err = snd_pcm_prepare (handle); + if (err < 0) { + alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle); + goto err; + } + + if (!in && conf.threshold) { + snd_pcm_uframes_t threshold; + int bytes_per_sec; + + bytes_per_sec = freq + << (nchannels == 2) + << (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16); + + threshold = (conf.threshold * bytes_per_sec) / 1000; + alsa_set_threshold (handle, threshold); + } + + obt->fmt = req->fmt; + obt->nchannels = nchannels; + obt->freq = freq; + obt->samples = obt_buffer_size; + *handlep = handle; + +#if defined DEBUG_MISMATCHES || defined DEBUG + if (obt->fmt != req->fmt || + obt->nchannels != req->nchannels || + obt->freq != req->freq) { + dolog ("Audio paramters mismatch for %s\n", typ); + alsa_dump_info (req, obt); + } +#endif + +#ifdef DEBUG + alsa_dump_info (req, obt); +#endif + return 0; + + err: + alsa_anal_close (&handle); + return -1; +} + +static int alsa_recover (snd_pcm_t *handle) +{ + int err = snd_pcm_prepare (handle); + if (err < 0) { + alsa_logerr (err, "Failed to prepare handle %p\n", handle); + return -1; + } + return 0; +} + +static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle) +{ + snd_pcm_sframes_t avail; + + avail = snd_pcm_avail_update (handle); + if (avail < 0) { + if (avail == -EPIPE) { + if (!alsa_recover (handle)) { + avail = snd_pcm_avail_update (handle); + } + } + + if (avail < 0) { + alsa_logerr (avail, + "Could not obtain number of available frames\n"); + return -1; + } + } + + return avail; +} + +static int alsa_run_out (HWVoiceOut *hw) +{ + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; + int rpos, live, decr; + int samples; + uint8_t *dst; + st_sample_t *src; + snd_pcm_sframes_t avail; + + live = audio_pcm_hw_get_live_out (hw); + if (!live) { + return 0; + } + + avail = alsa_get_avail (alsa->handle); + if (avail < 0) { + dolog ("Could not get number of available playback frames\n"); + return 0; + } + + decr = audio_MIN (live, avail); + samples = decr; + rpos = hw->rpos; + while (samples) { + int left_till_end_samples = hw->samples - rpos; + int len = audio_MIN (samples, left_till_end_samples); + snd_pcm_sframes_t written; + + src = hw->mix_buf + rpos; + dst = advance (alsa->pcm_buf, rpos << hw->info.shift); + + hw->clip (dst, src, len); + + while (len) { + written = snd_pcm_writei (alsa->handle, dst, len); + + if (written <= 0) { + switch (written) { + case 0: + if (conf.verbose) { + dolog ("Failed to write %d frames (wrote zero)\n", len); + } + goto exit; + + case -EPIPE: + if (alsa_recover (alsa->handle)) { + alsa_logerr (written, "Failed to write %d frames\n", + len); + goto exit; + } + if (conf.verbose) { + dolog ("Recovering from playback xrun\n"); + } + continue; + + case -EAGAIN: + goto exit; + + default: + alsa_logerr (written, "Failed to write %d frames to %p\n", + len, dst); + goto exit; + } + } + + mixeng_clear (src, written); + rpos = (rpos + written) % hw->samples; + samples -= written; + len -= written; + dst = advance (dst, written << hw->info.shift); + src += written; + } + } + + exit: + hw->rpos = rpos; + return decr; +} + +static void alsa_fini_out (HWVoiceOut *hw) +{ + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; + + ldebug ("alsa_fini\n"); + alsa_anal_close (&alsa->handle); + + if (alsa->pcm_buf) { + qemu_free (alsa->pcm_buf); + alsa->pcm_buf = NULL; + } +} + +static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as) +{ + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; + struct alsa_params_req req; + struct alsa_params_obt obt; + audfmt_e effective_fmt; + int endianness; + int err; + snd_pcm_t *handle; + audsettings_t obt_as; + + req.fmt = aud_to_alsafmt (as->fmt); + req.freq = as->freq; + req.nchannels = as->nchannels; + req.period_size = conf.period_size_out; + req.buffer_size = conf.buffer_size_out; + + if (alsa_open (0, &req, &obt, &handle)) { + return -1; + } + + err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness); + if (err) { + alsa_anal_close (&handle); + return -1; + } + + obt_as.freq = obt.freq; + obt_as.nchannels = obt.nchannels; + obt_as.fmt = effective_fmt; + + audio_pcm_init_info ( + &hw->info, + &obt_as, + audio_need_to_swap_endian (endianness) + ); + hw->samples = obt.samples; + + alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift); + if (!alsa->pcm_buf) { + dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n", + hw->samples, 1 << hw->info.shift); + alsa_anal_close (&handle); + return -1; + } + + alsa->handle = handle; + return 0; +} + +static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause) +{ + int err; + + if (pause) { + err = snd_pcm_drop (handle); + if (err < 0) { + alsa_logerr (err, "Could not stop %s\n", typ); + return -1; + } + } + else { + err = snd_pcm_prepare (handle); + if (err < 0) { + alsa_logerr (err, "Could not prepare handle for %s\n", typ); + return -1; + } + } + + return 0; +} + +static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; + + switch (cmd) { + case VOICE_ENABLE: + ldebug ("enabling voice\n"); + return alsa_voice_ctl (alsa->handle, "playback", 0); + + case VOICE_DISABLE: + ldebug ("disabling voice\n"); + return alsa_voice_ctl (alsa->handle, "playback", 1); + } + + return -1; +} + +static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as) +{ + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; + struct alsa_params_req req; + struct alsa_params_obt obt; + int endianness; + int err; + audfmt_e effective_fmt; + snd_pcm_t *handle; + audsettings_t obt_as; + + req.fmt = aud_to_alsafmt (as->fmt); + req.freq = as->freq; + req.nchannels = as->nchannels; + req.period_size = conf.period_size_in; + req.buffer_size = conf.buffer_size_in; + + if (alsa_open (1, &req, &obt, &handle)) { + return -1; + } + + err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness); + if (err) { + alsa_anal_close (&handle); + return -1; + } + + obt_as.freq = obt.freq; + obt_as.nchannels = obt.nchannels; + obt_as.fmt = effective_fmt; + + audio_pcm_init_info ( + &hw->info, + &obt_as, + audio_need_to_swap_endian (endianness) + ); + hw->samples = obt.samples; + + alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + if (!alsa->pcm_buf) { + dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", + hw->samples, 1 << hw->info.shift); + alsa_anal_close (&handle); + return -1; + } + + alsa->handle = handle; + return 0; +} + +static void alsa_fini_in (HWVoiceIn *hw) +{ + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; + + alsa_anal_close (&alsa->handle); + + if (alsa->pcm_buf) { + qemu_free (alsa->pcm_buf); + alsa->pcm_buf = NULL; + } +} + +static int alsa_run_in (HWVoiceIn *hw) +{ + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; + int hwshift = hw->info.shift; + int i; + int live = audio_pcm_hw_get_live_in (hw); + int dead = hw->samples - live; + int decr; + struct { + int add; + int len; + } bufs[2] = { + { hw->wpos, 0 }, + { 0, 0 } + }; + snd_pcm_sframes_t avail; + snd_pcm_uframes_t read_samples = 0; + + if (!dead) { + return 0; + } + + avail = alsa_get_avail (alsa->handle); + if (avail < 0) { + dolog ("Could not get number of captured frames\n"); + return 0; + } + + if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) { + avail = hw->samples; + } + + decr = audio_MIN (dead, avail); + if (!decr) { + return 0; + } + + if (hw->wpos + decr > hw->samples) { + bufs[0].len = (hw->samples - hw->wpos); + bufs[1].len = (decr - (hw->samples - hw->wpos)); + } + else { + bufs[0].len = decr; + } + + for (i = 0; i < 2; ++i) { + void *src; + st_sample_t *dst; + snd_pcm_sframes_t nread; + snd_pcm_uframes_t len; + + len = bufs[i].len; + + src = advance (alsa->pcm_buf, bufs[i].add << hwshift); + dst = hw->conv_buf + bufs[i].add; + + while (len) { + nread = snd_pcm_readi (alsa->handle, src, len); + + if (nread <= 0) { + switch (nread) { + case 0: + if (conf.verbose) { + dolog ("Failed to read %ld frames (read zero)\n", len); + } + goto exit; + + case -EPIPE: + if (alsa_recover (alsa->handle)) { + alsa_logerr (nread, "Failed to read %ld frames\n", len); + goto exit; + } + if (conf.verbose) { + dolog ("Recovering from capture xrun\n"); + } + continue; + + case -EAGAIN: + goto exit; + + default: + alsa_logerr ( + nread, + "Failed to read %ld frames from %p\n", + len, + src + ); + goto exit; + } + } + + hw->conv (dst, src, nread, &nominal_volume); + + src = advance (src, nread << hwshift); + dst += nread; + + read_samples += nread; + len -= nread; + } + } + + exit: + hw->wpos = (hw->wpos + read_samples) % hw->samples; + return read_samples; +} + +static int alsa_read (SWVoiceIn *sw, void *buf, int size) +{ + return audio_pcm_sw_read (sw, buf, size); +} + +static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) +{ + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; + + switch (cmd) { + case VOICE_ENABLE: + ldebug ("enabling voice\n"); + return alsa_voice_ctl (alsa->handle, "capture", 0); + + case VOICE_DISABLE: + ldebug ("disabling voice\n"); + return alsa_voice_ctl (alsa->handle, "capture", 1); + } + + return -1; +} + +static void *alsa_audio_init (void) +{ + return &conf; +} + +static void alsa_audio_fini (void *opaque) +{ + (void) opaque; +} + +static struct audio_option alsa_options[] = { + {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out, + "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, + {"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out, + "DAC period size", &conf.period_size_out_overriden, 0}, + {"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out, + "DAC buffer size", &conf.buffer_size_out_overriden, 0}, + + {"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in, + "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, + {"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in, + "ADC period size", &conf.period_size_in_overriden, 0}, + {"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in, + "ADC buffer size", &conf.buffer_size_in_overriden, 0}, + + {"THRESHOLD", AUD_OPT_INT, &conf.threshold, + "(undocumented)", NULL, 0}, + + {"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out, + "DAC device name (for instance dmix)", NULL, 0}, + + {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in, + "ADC device name", NULL, 0}, + + {"VERBOSE", AUD_OPT_BOOL, &conf.verbose, + "Behave in a more verbose way", NULL, 0}, + + {NULL, 0, NULL, NULL, NULL, 0} +}; + +static struct audio_pcm_ops alsa_pcm_ops = { + alsa_init_out, + alsa_fini_out, + alsa_run_out, + alsa_write, + alsa_ctl_out, + + alsa_init_in, + alsa_fini_in, + alsa_run_in, + alsa_read, + alsa_ctl_in +}; + +struct audio_driver alsa_audio_driver = { + INIT_FIELD (name = ) "alsa", + INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org", + INIT_FIELD (options = ) alsa_options, + INIT_FIELD (init = ) alsa_audio_init, + INIT_FIELD (fini = ) alsa_audio_fini, + INIT_FIELD (pcm_ops = ) &alsa_pcm_ops, + INIT_FIELD (can_be_default = ) 1, + INIT_FIELD (max_voices_out = ) INT_MAX, + INIT_FIELD (max_voices_in = ) INT_MAX, + INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut), + INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn) +}; diff --git a/tools/ioemu/audio/audio.c b/tools/ioemu/audio/audio.c new file mode 100644 index 0000000000..7634535230 --- /dev/null +++ b/tools/ioemu/audio/audio.c @@ -0,0 +1,1481 @@ +/* + * QEMU Audio subsystem + * + * Copyright (c) 2003-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#define AUDIO_CAP "audio" +#include "audio_int.h" + +/* #define DEBUG_PLIVE */ +/* #define DEBUG_LIVE */ +/* #define DEBUG_OUT */ + +#define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown" + +static struct audio_driver *drvtab[] = { +#ifdef CONFIG_OSS + &oss_audio_driver, +#endif +#ifdef CONFIG_ALSA + &alsa_audio_driver, +#endif +#ifdef CONFIG_COREAUDIO + &coreaudio_audio_driver, +#endif +#ifdef CONFIG_DSOUND + &dsound_audio_driver, +#endif +#ifdef CONFIG_FMOD + &fmod_audio_driver, +#endif +#ifdef CONFIG_SDL + &sdl_audio_driver, +#endif + &no_audio_driver, + &wav_audio_driver +}; + +struct fixed_settings { + int enabled; + int nb_voices; + int greedy; + audsettings_t settings; +}; + +static struct { + struct fixed_settings fixed_out; + struct fixed_settings fixed_in; + union { + int hz; + int64_t ticks; + } period; + int plive; + int log_to_monitor; +} conf = { + { /* DAC fixed settings */ + 1, /* enabled */ + 1, /* nb_voices */ + 1, /* greedy */ + { + 44100, /* freq */ + 2, /* nchannels */ + AUD_FMT_S16 /* fmt */ + } + }, + + { /* ADC fixed settings */ + 1, /* enabled */ + 1, /* nb_voices */ + 1, /* greedy */ + { + 44100, /* freq */ + 2, /* nchannels */ + AUD_FMT_S16 /* fmt */ + } + }, + + { 0 }, /* period */ + 0, /* plive */ + 0 /* log_to_monitor */ +}; + +static AudioState glob_audio_state; + +volume_t nominal_volume = { + 0, +#ifdef FLOAT_MIXENG + 1.0, + 1.0 +#else + UINT_MAX, + UINT_MAX +#endif +}; + +/* http://www.df.lth.se/~john_e/gems/gem002d.html */ +/* http://www.multi-platforms.com/Tips/PopCount.htm */ +uint32_t popcount (uint32_t u) +{ + u = ((u&0x55555555) + ((u>>1)&0x55555555)); + u = ((u&0x33333333) + ((u>>2)&0x33333333)); + u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); + u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); + u = ( u&0x0000ffff) + (u>>16); + return u; +} + +inline uint32_t lsbindex (uint32_t u) +{ + return popcount ((u&-u)-1); +} + +#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED +#error No its not +#else +int audio_bug (const char *funcname, int cond) +{ + if (cond) { + static int shown; + + AUD_log (NULL, "Error a bug that was just triggered in %s\n", funcname); + if (!shown) { + shown = 1; + AUD_log (NULL, "Save all your work and restart without audio\n"); + AUD_log (NULL, "Please send bug report to malc@pulsesoft.com\n"); + AUD_log (NULL, "I am sorry\n"); + } + AUD_log (NULL, "Context:\n"); + +#if defined AUDIO_BREAKPOINT_ON_BUG +# if defined HOST_I386 +# if defined __GNUC__ + __asm__ ("int3"); +# elif defined _MSC_VER + _asm _emit 0xcc; +# else + abort (); +# endif +# else + abort (); +# endif +#endif + } + + return cond; +} +#endif + +void *audio_calloc (const char *funcname, int nmemb, size_t size) +{ + int cond; + size_t len; + + len = nmemb * size; + cond = !nmemb || !size; + cond |= nmemb < 0; + cond |= len < size; + + if (audio_bug ("audio_calloc", cond)) { + AUD_log (NULL, "%s passed invalid arguments to audio_calloc\n", + funcname); + AUD_log (NULL, "nmemb=%d size=%zu (len=%zu)\n", nmemb, size, len); + return NULL; + } + + return qemu_mallocz (len); +} + +static char *audio_alloc_prefix (const char *s) +{ + const char qemu_prefix[] = "QEMU_"; + size_t len; + char *r; + + if (!s) { + return NULL; + } + + len = strlen (s); + r = qemu_malloc (len + sizeof (qemu_prefix)); + + if (r) { + size_t i; + char *u = r + sizeof (qemu_prefix) - 1; + + strcpy (r, qemu_prefix); + strcat (r, s); + + for (i = 0; i < len; ++i) { + u[i] = toupper (u[i]); + } + } + return r; +} + +const char *audio_audfmt_to_string (audfmt_e fmt) +{ + switch (fmt) { + case AUD_FMT_U8: + return "U8"; + + case AUD_FMT_U16: + return "U16"; + + case AUD_FMT_S8: + return "S8"; + + case AUD_FMT_S16: + return "S16"; + } + + dolog ("Bogus audfmt %d returning S16\n", fmt); + return "S16"; +} + +audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval, int *defaultp) +{ + if (!strcasecmp (s, "u8")) { + *defaultp = 0; + return AUD_FMT_U8; + } + else if (!strcasecmp (s, "u16")) { + *defaultp = 0; + return AUD_FMT_U16; + } + else if (!strcasecmp (s, "s8")) { + *defaultp = 0; + return AUD_FMT_S8; + } + else if (!strcasecmp (s, "s16")) { + *defaultp = 0; + return AUD_FMT_S16; + } + else { + dolog ("Bogus audio format `%s' using %s\n", + s, audio_audfmt_to_string (defval)); + *defaultp = 1; + return defval; + } +} + +static audfmt_e audio_get_conf_fmt (const char *envname, + audfmt_e defval, + int *defaultp) +{ + const char *var = getenv (envname); + if (!var) { + *defaultp = 1; + return defval; + } + return audio_string_to_audfmt (var, defval, defaultp); +} + +static int audio_get_conf_int (const char *key, int defval, int *defaultp) +{ + int val; + char *strval; + + strval = getenv (key); + if (strval) { + *defaultp = 0; + val = atoi (strval); + return val; + } + else { + *defaultp = 1; + return defval; + } +} + +static const char *audio_get_conf_str (const char *key, + const char *defval, + int *defaultp) +{ + const char *val = getenv (key); + if (!val) { + *defaultp = 1; + return defval; + } + else { + *defaultp = 0; + return val; + } +} + +void AUD_vlog (const char *cap, const char *fmt, va_list ap) +{ + if (conf.log_to_monitor) { + if (cap) { + term_printf ("%s: ", cap); + } + + term_vprintf (fmt, ap); + } + else { + if (cap) { + fprintf (stderr, "%s: ", cap); + } + + vfprintf (stderr, fmt, ap); + } +} + +void AUD_log (const char *cap, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (cap, fmt, ap); + va_end (ap); +} + +static void audio_print_options (const char *prefix, + struct audio_option *opt) +{ + char *uprefix; + + if (!prefix) { + dolog ("No prefix specified\n"); + return; + } + + if (!opt) { + dolog ("No options\n"); + return; + } + + uprefix = audio_alloc_prefix (prefix); + + for (; opt->name; opt++) { + const char *state = "default"; + printf (" %s_%s: ", uprefix, opt->name); + + if (opt->overridenp && *opt->overridenp) { + state = "current"; + } + + switch (opt->tag) { + case AUD_OPT_BOOL: + { + int *intp = opt->valp; + printf ("boolean, %s = %d\n", state, *intp ? 1 : 0); + } + break; + + case AUD_OPT_INT: + { + int *intp = opt->valp; + printf ("integer, %s = %d\n", state, *intp); + } + break; + + case AUD_OPT_FMT: + { + audfmt_e *fmtp = opt->valp; + printf ( + "format, %s = %s, (one of: U8 S8 U16 S16)\n", + state, + audio_audfmt_to_string (*fmtp) + ); + } + break; + + case AUD_OPT_STR: + { + const char **strp = opt->valp; + printf ("string, %s = %s\n", + state, + *strp ? *strp : "(not set)"); + } + break; + + default: + printf ("???\n"); + dolog ("Bad value tag for option %s_%s %d\n", + uprefix, opt->name, opt->tag); + break; + } + printf (" %s\n", opt->descr); + } + + qemu_free (uprefix); +} + +static void audio_process_options (const char *prefix, + struct audio_option *opt) +{ + char *optname; + const char qemu_prefix[] = "QEMU_"; + size_t preflen; + + if (audio_bug (AUDIO_FUNC, !prefix)) { + dolog ("prefix = NULL\n"); + return; + } + + if (audio_bug (AUDIO_FUNC, !opt)) { + dolog ("opt = NULL\n"); + return; + } + + preflen = strlen (prefix); + + for (; opt->name; opt++) { + size_t len, i; + int def; + + if (!opt->valp) { + dolog ("Option value pointer for `%s' is not set\n", + opt->name); + continue; + } + + len = strlen (opt->name); + /* len of opt->name + len of prefix + size of qemu_prefix + * (includes trailing zero) + zero + underscore (on behalf of + * sizeof) */ + optname = qemu_malloc (len + preflen + sizeof (qemu_prefix) + 1); + if (!optname) { + dolog ("Could not allocate memory for option name `%s'\n", + opt->name); + continue; + } + + strcpy (optname, qemu_prefix); + + /* copy while upper-casing, including trailing zero */ + for (i = 0; i <= preflen; ++i) { + optname[i + sizeof (qemu_prefix) - 1] = toupper (prefix[i]); + } + strcat (optname, "_"); + strcat (optname, opt->name); + + def = 1; + switch (opt->tag) { + case AUD_OPT_BOOL: + case AUD_OPT_INT: + { + int *intp = opt->valp; + *intp = audio_get_conf_int (optname, *intp, &def); + } + break; + + case AUD_OPT_FMT: + { + audfmt_e *fmtp = opt->valp; + *fmtp = audio_get_conf_fmt (optname, *fmtp, &def); + } + break; + + case AUD_OPT_STR: + { + const char **strp = opt->valp; + *strp = audio_get_conf_str (optname, *strp, &def); + } + break; + + default: + dolog ("Bad value tag for option `%s' - %d\n", + optname, opt->tag); + break; + } + + if (!opt->overridenp) { + opt->overridenp = &opt->overriden; + } + *opt->overridenp = !def; + qemu_free (optname); + } +} + +static void audio_print_settings (audsettings_t *as) +{ + dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels); + + switch (as->fmt) { + case AUD_FMT_S8: + AUD_log (NULL, "S8"); + break; + case AUD_FMT_U8: + AUD_log (NULL, "U8"); + break; + case AUD_FMT_S16: + AUD_log (NULL, "S16"); + break; + case AUD_FMT_U16: + AUD_log (NULL, "U16"); + break; + default: + AUD_log (NULL, "invalid(%d)", as->fmt); + break; + } + AUD_log (NULL, "\n"); +} + +static int audio_validate_settigs (audsettings_t *as) +{ + int invalid; + + invalid = as->nchannels != 1 && as->nchannels != 2; + + switch (as->fmt) { + case AUD_FMT_S8: + case AUD_FMT_U8: + case AUD_FMT_S16: + case AUD_FMT_U16: + break; + default: + invalid = 1; + break; + } + + invalid |= as->freq <= 0; + + if (invalid) { + return -1; + } + return 0; +} + +static int audio_pcm_info_eq (struct audio_pcm_info *info, audsettings_t *as) +{ + int bits = 8, sign = 0; + + switch (as->fmt) { + case AUD_FMT_S8: + sign = 1; + case AUD_FMT_U8: + break; + + case AUD_FMT_S16: + sign = 1; + case AUD_FMT_U16: + bits = 16; + break; + } + return info->freq == as->freq + && info->nchannels == as->nchannels + && info->sign == sign + && info->bits == bits; +} + +void audio_pcm_init_info ( + struct audio_pcm_info *info, + audsettings_t *as, + int swap_endian + ) +{ + int bits = 8, sign = 0; + + switch (as->fmt) { + case AUD_FMT_S8: + sign = 1; + case AUD_FMT_U8: + break; + + case AUD_FMT_S16: + sign = 1; + case AUD_FMT_U16: + bits = 16; + break; + } + + info->freq = as->freq; + info->bits = bits; + info->sign = sign; + info->nchannels = as->nchannels; + info->shift = (as->nchannels == 2) + (bits == 16); + info->align = (1 << info->shift) - 1; + info->bytes_per_second = info->freq << info->shift; + info->swap_endian = swap_endian; +} + +void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) +{ + if (!len) { + return; + } + + if (info->sign) { + memset (buf, len << info->shift, 0x00); + } + else { + if (info->bits == 8) { + memset (buf, len << info->shift, 0x80); + } + else { + int i; + uint16_t *p = buf; + int shift = info->nchannels - 1; + short s = INT16_MAX; + + if (info->swap_endian) { + s = bswap16 (s); + } + + for (i = 0; i < len << shift; i++) { + p[i] = s; + } + } + } +} + +/* + * Hard voice (capture) + */ +static int audio_pcm_hw_find_min_in (HWVoiceIn *hw) +{ + SWVoiceIn *sw; + int m = hw->total_samples_captured; + + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + if (sw->active) { + m = audio_MIN (m, sw->total_hw_samples_acquired); + } + } + return m; +} + +int audio_pcm_hw_get_live_in (HWVoiceIn *hw) +{ + int live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw); + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { + dolog ("live=%d hw->samples=%d\n", live, hw->samples); + return 0; + } + return live; +} + +/* + * Soft voice (capture) + */ +static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw) +{ + HWVoiceIn *hw = sw->hw; + int live = hw->total_samples_captured - sw->total_hw_samples_acquired; + int rpos; + + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { + dolog ("live=%d hw->samples=%d\n", live, hw->samples); + return 0; + } + + rpos = hw->wpos - live; + if (rpos >= 0) { + return rpos; + } + else { + return hw->samples + rpos; + } +} + +int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size) +{ + HWVoiceIn *hw = sw->hw; + int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0; + st_sample_t *src, *dst = sw->buf; + + rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples; + + live = hw->total_samples_captured - sw->total_hw_samples_acquired; + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { + dolog ("live_in=%d hw->samples=%d\n", live, hw->samples); + return 0; + } + + samples = size >> sw->info.shift; + if (!live) { + return 0; + } + + swlim = (live * sw->ratio) >> 32; + swlim = audio_MIN (swlim, samples); + + while (swlim) { + src = hw->conv_buf + rpos; + isamp = hw->wpos - rpos; + /* XXX: <= ? */ + if (isamp <= 0) { + isamp = hw->samples - rpos; + } + + if (!isamp) { + break; + } + osamp = swlim; + + if (audio_bug (AUDIO_FUNC, osamp < 0)) { + dolog ("osamp=%d\n", osamp); + return 0; + } + + st_rate_flow (sw->rate, src, dst, &isamp, &osamp); + swlim -= osamp; + rpos = (rpos + isamp) % hw->samples; + dst += osamp; + ret += osamp; + total += isamp; + } + + sw->clip (buf, sw->buf, ret); + sw->total_hw_samples_acquired += total; + return ret << sw->info.shift; +} + +/* + * Hard voice (playback) + */ +static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep) +{ + SWVoiceOut *sw; + int m = INT_MAX; + int nb_live = 0; + + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + if (sw->active || !sw->empty) { + m = audio_MIN (m, sw->total_hw_samples_mixed); + nb_live += 1; + } + } + + *nb_livep = nb_live; + return m; +} + +int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live) +{ + int smin; + + smin = audio_pcm_hw_find_min_out (hw, nb_live); + + if (!*nb_live) { + return 0; + } + else { + int live = smin; + + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { + dolog ("live=%d hw->samples=%d\n", live, hw->samples); + return 0; + } + return live; + } +} + +int audio_pcm_hw_get_live_out (HWVoiceOut *hw) +{ + int nb_live; + int live; + + live = audio_pcm_hw_get_live_out2 (hw, &nb_live); + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { + dolog ("live=%d hw->samples=%d\n", live, hw->samples); + return 0; + } + return live; +} + +/* + * Soft voice (playback) + */ +int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) +{ + int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; + int ret = 0, pos = 0, total = 0; + + if (!sw) { + return size; + } + + hwsamples = sw->hw->samples; + + live = sw->total_hw_samples_mixed; + if (audio_bug (AUDIO_FUNC, live < 0 || live > hwsamples)){ + dolog ("live=%d hw->samples=%d\n", live, hwsamples); + return 0; + } + + if (live == hwsamples) { + return 0; + } + + wpos = (sw->hw->rpos + live) % hwsamples; + samples = size >> sw->info.shift; + + dead = hwsamples - live; + swlim = ((int64_t) dead << 32) / sw->ratio; + swlim = audio_MIN (swlim, samples); + if (swlim) { + sw->conv (sw->buf, buf, swlim, &sw->vol); + } + + while (swlim) { + dead = hwsamples - live; + left = hwsamples - wpos; + blck = audio_MIN (dead, left); + if (!blck) { + break; + } + isamp = swlim; + osamp = blck; + st_rate_flow_mix ( + sw->rate, + sw->buf + pos, + sw->hw->mix_buf + wpos, + &isamp, + &osamp + ); + ret += isamp; + swlim -= isamp; + pos += isamp; + live += osamp; + wpos = (wpos + osamp) % hwsamples; + total += osamp; + } + + sw->total_hw_samples_mixed += total; + sw->empty = sw->total_hw_samples_mixed == 0; + +#ifdef DEBUG_OUT + dolog ( + "%s: write size %d ret %d total sw %d\n", + SW_NAME (sw), + size >> sw->info.shift, + ret, + sw->total_hw_samples_mixed + ); +#endif + + return ret << sw->info.shift; +} + +#ifdef DEBUG_AUDIO +static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) +{ + dolog ("%s: bits %d, sign %d, freq %d, nchan %d\n", + cap, info->bits, info->sign, info->freq, info->nchannels); +} +#endif + +#define DAC +#include "audio_template.h" +#undef DAC +#include "audio_template.h" + +int AUD_write (SWVoiceOut *sw, void *buf, int size) +{ + int bytes; + + if (!sw) { + /* XXX: Consider options */ + return size; + } + + if (!sw->hw->enabled) { + dolog ("Writing to disabled voice %s\n", SW_NAME (sw)); + return 0; + } + + bytes = sw->hw->pcm_ops->write (sw, buf, size); + return bytes; +} + +int AUD_read (SWVoiceIn *sw, void *buf, int size) +{ + int bytes; + + if (!sw) { + /* XXX: Consider options */ + return size; + } + + if (!sw->hw->enabled) { + dolog ("Reading from disabled voice %s\n", SW_NAME (sw)); + return 0; + } + + bytes = sw->hw->pcm_ops->read (sw, buf, size); + return bytes; +} + +int AUD_get_buffer_size_out (SWVoiceOut *sw) +{ + return sw->hw->samples << sw->hw->info.shift; +} + +void AUD_set_active_out (SWVoiceOut *sw, int on) +{ + HWVoiceOut *hw; + + if (!sw) { + return; + } + + hw = sw->hw; + if (sw->active != on) { + SWVoiceOut *temp_sw; + + if (on) { + int total; + + hw->pending_disable = 0; + if (!hw->enabled) { + hw->enabled = 1; + hw->pcm_ops->ctl_out (hw, VOICE_ENABLE); + } + + if (sw->empty) { + total = 0; + } + } + else { + if (hw->enabled) { + int nb_active = 0; + + for (temp_sw = hw->sw_head.lh_first; temp_sw; + temp_sw = temp_sw->entries.le_next) { + nb_active += temp_sw->active != 0; + } + + hw->pending_disable = nb_active == 1; + } + } + sw->active = on; + } +} + +void AUD_set_active_in (SWVoiceIn *sw, int on) +{ + HWVoiceIn *hw; + + if (!sw) { + return; + } + + hw = sw->hw; + if (sw->active != on) { + SWVoiceIn *temp_sw; + + if (on) { + if (!hw->enabled) { + hw->enabled = 1; + hw->pcm_ops->ctl_in (hw, VOICE_ENABLE); + } + sw->total_hw_samples_acquired = hw->total_samples_captured; + } + else { + if (hw->enabled) { + int nb_active = 0; + + for (temp_sw = hw->sw_head.lh_first; temp_sw; + temp_sw = temp_sw->entries.le_next) { + nb_active += temp_sw->active != 0; + } + + if (nb_active == 1) { + hw->enabled = 0; + hw->pcm_ops->ctl_in (hw, VOICE_DISABLE); + } + } + } + sw->active = on; + } +} + +static int audio_get_avail (SWVoiceIn *sw) +{ + int live; + + if (!sw) { + return 0; + } + + live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; + if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) { + dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples); + return 0; + } + + ldebug ( + "%s: get_avail live %d ret %lld\n", + SW_NAME (sw), + live, (((int64_t) live << 32) / sw->ratio) << sw->info.shift + ); + + return (((int64_t) live << 32) / sw->ratio) << sw->info.shift; +} + +static int audio_get_free (SWVoiceOut *sw) +{ + int live, dead; + + if (!sw) { + return 0; + } + + live = sw->total_hw_samples_mixed; + + if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) { + dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples); + return 0; + } + + dead = sw->hw->samples - live; + +#ifdef DEBUG_OUT + dolog ("%s: get_free live %d dead %d ret %lld\n", + SW_NAME (sw), + live, dead, (((int64_t) dead << 32) / sw->ratio) << sw->info.shift); +#endif + + return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift; +} + +static void audio_run_out (AudioState *s) +{ + HWVoiceOut *hw = NULL; + SWVoiceOut *sw; + + while ((hw = audio_pcm_hw_find_any_enabled_out (s, hw))) { + int played; + int live, free, nb_live, cleanup_required; + + live = audio_pcm_hw_get_live_out2 (hw, &nb_live); + if (!nb_live) { + live = 0; + } + + if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { + dolog ("live=%d hw->samples=%d\n", live, hw->samples); + continue; + } + + if (hw->pending_disable && !nb_live) { +#ifdef DEBUG_OUT + dolog ("Disabling voice\n"); +#endif + hw->enabled = 0; + hw->pending_disable = 0; + hw->pcm_ops->ctl_out (hw, VOICE_DISABLE); + continue; + } + + if (!live) { + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + if (sw->active) { + free = audio_get_free (sw); + if (free > 0) { + sw->callback.fn (sw->callback.opaque, free); + } + } + } + continue; + } + + played = hw->pcm_ops->run_out (hw); + if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) { + dolog ("hw->rpos=%d hw->samples=%d played=%d\n", + hw->rpos, hw->samples, played); + hw->rpos = 0; + } + +#ifdef DEBUG_OUT + dolog ("played=%d\n", played); +#endif + + if (played) { + hw->ts_helper += played; + } + + cleanup_required = 0; + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + if (!sw->active && sw->empty) { + continue; + } + + if (audio_bug (AUDIO_FUNC, played > sw->total_hw_samples_mixed)) { + dolog ("played=%d sw->total_hw_samples_mixed=%d\n", + played, sw->total_hw_samples_mixed); + played = sw->total_hw_samples_mixed; + } + + sw->total_hw_samples_mixed -= played; + + if (!sw->total_hw_samples_mixed) { + sw->empty = 1; + cleanup_required |= !sw->active && !sw->callback.fn; + } + + if (sw->active) { + free = audio_get_free (sw); + if (free > 0) { + sw->callback.fn (sw->callback.opaque, free); + } + } + } + + if (cleanup_required) { + restart: + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + if (!sw->active && !sw->callback.fn) { +#ifdef DEBUG_PLIVE + dolog ("Finishing with old voice\n"); +#endif + audio_close_out (s, sw); + goto restart; /* play it safe */ + } + } + } + } +} + +static void audio_run_in (AudioState *s) +{ + HWVoiceIn *hw = NULL; + + while ((hw = audio_pcm_hw_find_any_enabled_in (s, hw))) { + SWVoiceIn *sw; + int captured, min; + + captured = hw->pcm_ops->run_in (hw); + + min = audio_pcm_hw_find_min_in (hw); + hw->total_samples_captured += captured - min; + hw->ts_helper += captured; + + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { + sw->total_hw_samples_acquired -= min; + + if (sw->active) { + int avail; + + avail = audio_get_avail (sw); + if (avail > 0) { + sw->callback.fn (sw->callback.opaque, avail); + } + } + } + } +} + +static void audio_timer (void *opaque) +{ + AudioState *s = opaque; + + audio_run_out (s); + audio_run_in (s); + + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); +} + +static struct audio_option audio_options[] = { + /* DAC */ + {"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_out.enabled, + "Use fixed settings for host DAC", NULL, 0}, + + {"DAC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_out.settings.freq, + "Frequency for fixed host DAC", NULL, 0}, + + {"DAC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_out.settings.fmt, + "Format for fixed host DAC", NULL, 0}, + + {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_out.settings.nchannels, + "Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0}, + + {"DAC_VOICES", AUD_OPT_INT, &conf.fixed_out.nb_voices, + "Number of voices for DAC", NULL, 0}, + + /* ADC */ + {"ADC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_in.enabled, + "Use fixed settings for host ADC", NULL, 0}, + + {"ADC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_in.settings.freq, + "Frequency for fixed host ADC", NULL, 0}, + + {"ADC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_in.settings.fmt, + "Format for fixed host ADC", NULL, 0}, + + {"ADC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_in.settings.nchannels, + "Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0}, + + {"ADC_VOICES", AUD_OPT_INT, &conf.fixed_in.nb_voices, + "Number of voices for ADC", NULL, 0}, + + /* Misc */ + {"TIMER_PERIOD", AUD_OPT_INT, &conf.period.hz, + "Timer period in HZ (0 - use lowest possible)", NULL, 0}, + + {"PLIVE", AUD_OPT_BOOL, &conf.plive, + "(undocumented)", NULL, 0}, + + {"LOG_TO_MONITOR", AUD_OPT_BOOL, &conf.log_to_monitor, + "print logging messages to montior instead of stderr", NULL, 0}, + + {NULL, 0, NULL, NULL, NULL, 0} +}; + +static void audio_pp_nb_voices (const char *typ, int nb) +{ + switch (nb) { + case 0: + printf ("Does not support %s\n", typ); + break; + case 1: + printf ("One %s voice\n", typ); + break; + case INT_MAX: + printf ("Theoretically supports many %s voices\n", typ); + break; + default: + printf ("Theoretically supports upto %d %s voices\n", nb, typ); + break; + } + +} + +void AUD_help (void) +{ + size_t i; + + audio_process_options ("AUDIO", audio_options); + for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { + struct audio_driver *d = drvtab[i]; + if (d->options) { + audio_process_options (d->name, d->options); + } + } + + printf ("Audio options:\n"); + audio_print_options ("AUDIO", audio_options); + printf ("\n"); + + printf ("Available drivers:\n"); + + for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { + struct audio_driver *d = drvtab[i]; + + printf ("Name: %s\n", d->name); + printf ("Description: %s\n", d->descr); + + audio_pp_nb_voices ("playback", d->max_voices_out); + audio_pp_nb_voices ("capture", d->max_voices_in); + + if (d->options) { + printf ("Options:\n"); + audio_print_options (d->name, d->options); + } + else { + printf ("No options\n"); + } + printf ("\n"); + } + + printf ( + "Options are settable through environment variables.\n" + "Example:\n" +#ifdef _WIN32 + " set QEMU_AUDIO_DRV=wav\n" + " set QEMU_WAV_PATH=c:\\tune.wav\n" +#else + " export QEMU_AUDIO_DRV=wav\n" + " export QEMU_WAV_PATH=$HOME/tune.wav\n" + "(for csh replace export with setenv in the above)\n" +#endif + " qemu ...\n\n" + ); +} + +static int audio_driver_init (AudioState *s, struct audio_driver *drv) +{ + if (drv->options) { + audio_process_options (drv->name, drv->options); + } + s->drv_opaque = drv->init (); + + if (s->drv_opaque) { + audio_init_nb_voices_out (s, drv); + audio_init_nb_voices_in (s, drv); + s->drv = drv; + return 0; + } + else { + dolog ("Could not init `%s' audio driver\n", drv->name); + return -1; + } +} + +static void audio_vm_change_state_handler (void *opaque, int running) +{ + AudioState *s = opaque; + HWVoiceOut *hwo = NULL; + HWVoiceIn *hwi = NULL; + int op = running ? VOICE_ENABLE : VOICE_DISABLE; + + while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) { + hwo->pcm_ops->ctl_out (hwo, op); + } + + while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) { + hwi->pcm_ops->ctl_in (hwi, op); + } +} + +static void audio_atexit (void) +{ + AudioState *s = &glob_audio_state; + HWVoiceOut *hwo = NULL; + HWVoiceIn *hwi = NULL; + + while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) { + hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE); + hwo->pcm_ops->fini_out (hwo); + } + + while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) { + hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE); + hwi->pcm_ops->fini_in (hwi); + } + + if (s->drv) { + s->drv->fini (s->drv_opaque); + } +} + +static void audio_save (QEMUFile *f, void *opaque) +{ + (void) f; + (void) opaque; +} + +static int audio_load (QEMUFile *f, void *opaque, int version_id) +{ + (void) f; + (void) opaque; + + if (version_id != 1) { + return -EINVAL; + } + + return 0; +} + +void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card) +{ + card->audio = s; + card->name = qemu_strdup (name); + memset (&card->entries, 0, sizeof (card->entries)); + LIST_INSERT_HEAD (&s->card_head, card, entries); +} + +void AUD_remove_card (QEMUSoundCard *card) +{ + LIST_REMOVE (card, entries); + card->audio = NULL; + qemu_free (card->name); +} + +AudioState *AUD_init (void) +{ + size_t i; + int done = 0; + const char *drvname; + AudioState *s = &glob_audio_state; + + LIST_INIT (&s->hw_head_out); + LIST_INIT (&s->hw_head_in); + atexit (audio_atexit); + + s->ts = qemu_new_timer (vm_clock, audio_timer, s); + if (!s->ts) { + dolog ("Could not create audio timer\n"); + return NULL; + } + + audio_process_options ("AUDIO", audio_options); + + s->nb_hw_voices_out = conf.fixed_out.nb_voices; + s->nb_hw_voices_in = conf.fixed_in.nb_voices; + + if (s->nb_hw_voices_out <= 0) { + dolog ("Bogus number of playback voices %d, setting to 1\n", + s->nb_hw_voices_out); + s->nb_hw_voices_out = 1; + } + + if (s->nb_hw_voices_in <= 0) { + dolog ("Bogus number of capture voices %d, setting to 0\n", + s->nb_hw_voices_in); + s->nb_hw_voices_in = 0; + } + + { + int def; + drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def); + } + + if (drvname) { + int found = 0; + + for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { + if (!strcmp (drvname, drvtab[i]->name)) { + done = !audio_driver_init (s, drvtab[i]); + found = 1; + break; + } + } + + if (!found) { + dolog ("Unknown audio driver `%s'\n", drvname); + dolog ("Run with -audio-help to list available drivers\n"); + } + } + + if (!done) { + for (i = 0; !done && i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { + if (drvtab[i]->can_be_default) { + done = !audio_driver_init (s, drvtab[i]); + } + } + } + + if (!done) { + done = !audio_driver_init (s, &no_audio_driver); + if (!done) { + dolog ("Could not initialize audio subsystem\n"); + } + else { + dolog ("warning: Using timer based audio emulation\n"); + } + } + + if (done) { + VMChangeStateEntry *e; + + if (conf.period.hz <= 0) { + if (conf.period.hz < 0) { + dolog ("warning: Timer period is negative - %d " + "treating as zero\n", + conf.period.hz); + } + conf.period.ticks = 1; + } + else { + conf.period.ticks = ticks_per_sec / conf.period.hz; + } + + e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s); + if (!e) { + dolog ("warning: Could not register change state handler\n" + "(Audio can continue looping even after stopping the VM)\n"); + } + } + else { + qemu_del_timer (s->ts); + return NULL; + } + + LIST_INIT (&s->card_head); + register_savevm ("audio", 0, 1, audio_save, audio_load, s); + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); + return s; +} diff --git a/tools/ioemu/audio/audio.h b/tools/ioemu/audio/audio.h new file mode 100644 index 0000000000..169b5f636a --- /dev/null +++ b/tools/ioemu/audio/audio.h @@ -0,0 +1,133 @@ +/* + * QEMU Audio subsystem header + * + * Copyright (c) 2003-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_AUDIO_H +#define QEMU_AUDIO_H + +#include "sys-queue.h" + +typedef void (*audio_callback_fn_t) (void *opaque, int avail); + +typedef enum { + AUD_FMT_U8, + AUD_FMT_S8, + AUD_FMT_U16, + AUD_FMT_S16 +} audfmt_e; + +typedef struct { + int freq; + int nchannels; + audfmt_e fmt; +} audsettings_t; + +typedef struct AudioState AudioState; +typedef struct SWVoiceOut SWVoiceOut; +typedef struct SWVoiceIn SWVoiceIn; + +typedef struct QEMUSoundCard { + AudioState *audio; + char *name; + LIST_ENTRY (QEMUSoundCard) entries; +} QEMUSoundCard; + +typedef struct QEMUAudioTimeStamp { + uint64_t old_ts; +} QEMUAudioTimeStamp; + +void AUD_vlog (const char *cap, const char *fmt, va_list ap); +void AUD_log (const char *cap, const char *fmt, ...) +#ifdef __GNUC__ + __attribute__ ((__format__ (__printf__, 2, 3))) +#endif + ; + +AudioState *AUD_init (void); +void AUD_help (void); +void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card); +void AUD_remove_card (QEMUSoundCard *card); + +SWVoiceOut *AUD_open_out ( + QEMUSoundCard *card, + SWVoiceOut *sw, + const char *name, + void *callback_opaque, + audio_callback_fn_t callback_fn, + audsettings_t *settings, + int sw_endian + ); + +void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw); +int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size); +int AUD_get_buffer_size_out (SWVoiceOut *sw); +void AUD_set_active_out (SWVoiceOut *sw, int on); +int AUD_is_active_out (SWVoiceOut *sw); + +void AUD_init_time_stamp_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts); +uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts); + +SWVoiceIn *AUD_open_in ( + QEMUSoundCard *card, + SWVoiceIn *sw, + const char *name, + void *callback_opaque, + audio_callback_fn_t callback_fn, + audsettings_t *settings, + int sw_endian + ); + +void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw); +int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size); +void AUD_set_active_in (SWVoiceIn *sw, int on); +int AUD_is_active_in (SWVoiceIn *sw); + +void AUD_init_time_stamp_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts); +uint64_t AUD_get_elapsed_usec_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts); + +static inline void *advance (void *p, int incr) +{ + uint8_t *d = p; + return (d + incr); +} + +uint32_t popcount (uint32_t u); +inline uint32_t lsbindex (uint32_t u); + +#ifdef __GNUC__ +#define audio_MIN(a, b) ( __extension__ ({ \ + __typeof (a) ta = a; \ + __typeof (b) tb = b; \ + ((ta)>(tb)?(tb):(ta)); \ +})) + +#define audio_MAX(a, b) ( __extension__ ({ \ + __typeof (a) ta = a; \ + __typeof (b) tb = b; \ + ((ta)<(tb)?(tb):(ta)); \ +})) +#else +#define audio_MIN(a, b) ((a)>(b)?(b):(a)) +#define audio_MAX(a, b) ((a)<(b)?(b):(a)) +#endif + +#endif /* audio.h */ diff --git a/tools/ioemu/audio/audio_int.h b/tools/ioemu/audio/audio_int.h new file mode 100644 index 0000000000..ca240ccc7b --- /dev/null +++ b/tools/ioemu/audio/audio_int.h @@ -0,0 +1,268 @@ +/* + * QEMU Audio subsystem header + * + * Copyright (c) 2003-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_AUDIO_INT_H +#define QEMU_AUDIO_INT_H + +#ifdef CONFIG_COREAUDIO +#define FLOAT_MIXENG +/* #define RECIPROCAL */ +#endif +#include "mixeng.h" + +struct audio_pcm_ops; + +typedef enum { + AUD_OPT_INT, + AUD_OPT_FMT, + AUD_OPT_STR, + AUD_OPT_BOOL +} audio_option_tag_e; + +struct audio_option { + const char *name; + audio_option_tag_e tag; + void *valp; + const char *descr; + int *overridenp; + int overriden; +}; + +struct audio_callback { + void *opaque; + audio_callback_fn_t fn; +}; + +struct audio_pcm_info { + int bits; + int sign; + int freq; + int nchannels; + int align; + int shift; + int bytes_per_second; + int swap_endian; +}; + +typedef struct HWVoiceOut { + int enabled; + int pending_disable; + int valid; + struct audio_pcm_info info; + + f_sample *clip; + + int rpos; + uint64_t ts_helper; + + st_sample_t *mix_buf; + + int samples; + LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; + struct audio_pcm_ops *pcm_ops; + LIST_ENTRY (HWVoiceOut) entries; +} HWVoiceOut; + +typedef struct HWVoiceIn { + int enabled; + struct audio_pcm_info info; + + t_sample *conv; + + int wpos; + int total_samples_captured; + uint64_t ts_helper; + + st_sample_t *conv_buf; + + int samples; + LIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head; + struct audio_pcm_ops *pcm_ops; + LIST_ENTRY (HWVoiceIn) entries; +} HWVoiceIn; + +struct SWVoiceOut { + struct audio_pcm_info info; + t_sample *conv; + int64_t ratio; + st_sample_t *buf; + void *rate; + int total_hw_samples_mixed; + int active; + int empty; + HWVoiceOut *hw; + char *name; + volume_t vol; + struct audio_callback callback; + LIST_ENTRY (SWVoiceOut) entries; +}; + +struct SWVoiceIn { + int active; + struct audio_pcm_info info; + int64_t ratio; + void *rate; + int total_hw_samples_acquired; + st_sample_t *buf; + f_sample *clip; + HWVoiceIn *hw; + char *name; + volume_t vol; + struct audio_callback callback; + LIST_ENTRY (SWVoiceIn) entries; +}; + +struct audio_driver { + const char *name; + const char *descr; + struct audio_option *options; + void *(*init) (void); + void (*fini) (void *); + struct audio_pcm_ops *pcm_ops; + int can_be_default; + int max_voices_out; + int max_voices_in; + int voice_size_out; + int voice_size_in; +}; + +struct audio_pcm_ops { + int (*init_out)(HWVoiceOut *hw, audsettings_t *as); + void (*fini_out)(HWVoiceOut *hw); + int (*run_out) (HWVoiceOut *hw); + int (*write) (SWVoiceOut *sw, void *buf, int size); + int (*ctl_out) (HWVoiceOut *hw, int cmd, ...); + + int (*init_in) (HWVoiceIn *hw, audsettings_t *as); + void (*fini_in) (HWVoiceIn *hw); + int (*run_in) (HWVoiceIn *hw); + int (*read) (SWVoiceIn *sw, void *buf, int size); + int (*ctl_in) (HWVoiceIn *hw, int cmd, ...); +}; + +struct AudioState { + struct audio_driver *drv; + void *drv_opaque; + + QEMUTimer *ts; + LIST_HEAD (card_head, QEMUSoundCard) card_head; + LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in; + LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out; + int nb_hw_voices_out; + int nb_hw_voices_in; +}; + +extern struct audio_driver no_audio_driver; +extern struct audio_driver oss_audio_driver; +extern struct audio_driver sdl_audio_driver; +extern struct audio_driver wav_audio_driver; +extern struct audio_driver fmod_audio_driver; +extern struct audio_driver alsa_audio_driver; +extern struct audio_driver coreaudio_audio_driver; +extern struct audio_driver dsound_audio_driver; +extern volume_t nominal_volume; + +void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as, + int swap_endian); +void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); + +int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len); +int audio_pcm_hw_get_live_in (HWVoiceIn *hw); + +int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len); +int audio_pcm_hw_get_live_out (HWVoiceOut *hw); +int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live); + +int audio_bug (const char *funcname, int cond); +void *audio_calloc (const char *funcname, int nmemb, size_t size); + +#define VOICE_ENABLE 1 +#define VOICE_DISABLE 2 + +static inline int audio_ring_dist (int dst, int src, int len) +{ + return (dst >= src) ? (dst - src) : (len - src + dst); +} + +static inline int audio_need_to_swap_endian (int endianness) +{ +#ifdef WORDS_BIGENDIAN + return endianness != 1; +#else + return endianness != 0; +#endif +} + +#if defined __GNUC__ +#define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2))) +#define INIT_FIELD(f) . f +#define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (__printf__, n, m))) +#else +#define GCC_ATTR /**/ +#define INIT_FIELD(f) /**/ +#define GCC_FMT_ATTR(n, m) +#endif + +static void GCC_ATTR dolog (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); +} + +#ifdef DEBUG +static void GCC_ATTR ldebug (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); +} +#else +#if defined NDEBUG && defined __GNUC__ +#define ldebug(...) +#elif defined NDEBUG && defined _MSC_VER +#define ldebug __noop +#else +static void GCC_ATTR ldebug (const char *fmt, ...) +{ + (void) fmt; +} +#endif +#endif + +#undef GCC_ATTR + +#define AUDIO_STRINGIFY_(n) #n +#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) + +#if defined _MSC_VER || defined __GNUC__ +#define AUDIO_FUNC __FUNCTION__ +#else +#define AUDIO_FUNC __FILE__ ":" AUDIO_STRINGIFY (__LINE__) +#endif + +#endif /* audio_int.h */ diff --git a/tools/ioemu/audio/audio_template.h b/tools/ioemu/audio/audio_template.h new file mode 100644 index 0000000000..23d024201a --- /dev/null +++ b/tools/ioemu/audio/audio_template.h @@ -0,0 +1,565 @@ +/* + * QEMU Audio subsystem header + * + * Copyright (c) 2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef DAC +#define NAME "playback" +#define HWBUF hw->mix_buf +#define TYPE out +#define HW HWVoiceOut +#define SW SWVoiceOut +#else +#define NAME "capture" +#define TYPE in +#define HW HWVoiceIn +#define SW SWVoiceIn +#define HWBUF hw->conv_buf +#endif + +static void glue (audio_init_nb_voices_, TYPE) ( + AudioState *s, + struct audio_driver *drv + ) +{ + int max_voices = glue (drv->max_voices_, TYPE); + int voice_size = glue (drv->voice_size_, TYPE); + + if (glue (s->nb_hw_voices_, TYPE) > max_voices) { + if (!max_voices) { +#ifdef DAC + dolog ("Driver `%s' does not support " NAME "\n", drv->name); +#endif + } + else { + dolog ("Driver `%s' does not support %d " NAME " voices, max %d\n", + drv->name, + glue (s->nb_hw_voices_, TYPE), + max_voices); + } + glue (s->nb_hw_voices_, TYPE) = max_voices; + } + + if (audio_bug (AUDIO_FUNC, !voice_size && max_voices)) { + dolog ("drv=`%s' voice_size=0 max_voices=%d\n", + drv->name, max_voices); + glue (s->nb_hw_voices_, TYPE) = 0; + } + + if (audio_bug (AUDIO_FUNC, voice_size && !max_voices)) { + dolog ("drv=`%s' voice_size=%d max_voices=0\n", + drv->name, voice_size); + } +} + +static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw) +{ + if (HWBUF) { + qemu_free (HWBUF); + } + + HWBUF = NULL; +} + +static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw) +{ + HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t)); + if (!HWBUF) { + dolog ("Could not allocate " NAME " buffer (%d samples)\n", + hw->samples); + return -1; + } + + return 0; +} + +static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw) +{ + if (sw->buf) { + qemu_free (sw->buf); + } + + if (sw->rate) { + st_rate_stop (sw->rate); + } + + sw->buf = NULL; + sw->rate = NULL; +} + +static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw) +{ + int samples; + +#ifdef DAC + samples = sw->hw->samples; +#else + samples = ((int64_t) sw->hw->samples << 32) / sw->ratio; +#endif + + sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (st_sample_t)); + if (!sw->buf) { + dolog ("Could not allocate buffer for `%s' (%d samples)\n", + SW_NAME (sw), samples); + return -1; + } + +#ifdef DAC + sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq); +#else + sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq); +#endif + if (!sw->rate) { + qemu_free (sw->buf); + sw->buf = NULL; + return -1; + } + return 0; +} + +static int glue (audio_pcm_sw_init_, TYPE) ( + SW *sw, + HW *hw, + const char *name, + audsettings_t *as, + int endian + ) +{ + int err; + + audio_pcm_init_info (&sw->info, as, audio_need_to_swap_endian (endian)); + sw->hw = hw; + sw->active = 0; +#ifdef DAC + sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq; + sw->total_hw_samples_mixed = 0; + sw->empty = 1; +#else + sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq; +#endif + +#ifdef DAC + sw->conv = mixeng_conv +#else + sw->clip = mixeng_clip +#endif + [sw->info.nchannels == 2] + [sw->info.sign] + [sw->info.swap_endian] + [sw->info.bits == 16]; + + sw->name = qemu_strdup (name); + err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw); + if (err) { + qemu_free (sw->name); + sw->name = NULL; + } + return err; +} + +static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw) +{ + glue (audio_pcm_sw_free_resources_, TYPE) (sw); + if (sw->name) { + qemu_free (sw->name); + sw->name = NULL; + } +} + +static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw) +{ + LIST_INSERT_HEAD (&hw->sw_head, sw, entries); +} + +static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw) +{ + LIST_REMOVE (sw, entries); +} + +static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp) +{ + HW *hw = *hwp; + + if (!hw->sw_head.lh_first) { + LIST_REMOVE (hw, entries); + glue (s->nb_hw_voices_, TYPE) += 1; + glue (audio_pcm_hw_free_resources_ ,TYPE) (hw); + glue (hw->pcm_ops->fini_, TYPE) (hw); + qemu_free (hw); + *hwp = NULL; + } +} + +static HW *glue (audio_pcm_hw_find_any_, TYPE) (AudioState *s, HW *hw) +{ + return hw ? hw->entries.le_next : s->glue (hw_head_, TYPE).lh_first; +} + +static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (AudioState *s, HW *hw) +{ + while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) { + if (hw->enabled) { + return hw; + } + } + return NULL; +} + +static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( + AudioState *s, + HW *hw, + audsettings_t *as + ) +{ + while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) { + if (audio_pcm_info_eq (&hw->info, as)) { + return hw; + } + } + return NULL; +} + +static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as) +{ + HW *hw; + struct audio_driver *drv = s->drv; + + if (!glue (s->nb_hw_voices_, TYPE)) { + return NULL; + } + + if (audio_bug (AUDIO_FUNC, !drv)) { + dolog ("No host audio driver\n"); + return NULL; + } + + if (audio_bug (AUDIO_FUNC, !drv->pcm_ops)) { + dolog ("Host audio driver without pcm_ops\n"); + return NULL; + } + + hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE)); + if (!hw) { + dolog ("Can not allocate voice `%s' size %d\n", + drv->name, glue (drv->voice_size_, TYPE)); + return NULL; + } + + hw->pcm_ops = drv->pcm_ops; + LIST_INIT (&hw->sw_head); + + if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { + goto err0; + } + + if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) { + dolog ("hw->samples=%d\n", hw->samples); + goto err1; + } + +#ifdef DAC + hw->clip = mixeng_clip +#else + hw->conv = mixeng_conv +#endif + [hw->info.nchannels == 2] + [hw->info.sign] + [hw->info.swap_endian] + [hw->info.bits == 16]; + + if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) { + goto err1; + } + + LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries); + glue (s->nb_hw_voices_, TYPE) -= 1; + return hw; + + err1: + glue (hw->pcm_ops->fini_, TYPE) (hw); + err0: + qemu_free (hw); + return NULL; +} + +static HW *glue (audio_pcm_hw_add_, TYPE) (AudioState *s, audsettings_t *as) +{ + HW *hw; + + if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) { + hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as); + if (hw) { + return hw; + } + } + + hw = glue (audio_pcm_hw_find_specific_, TYPE) (s, NULL, as); + if (hw) { + return hw; + } + + hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as); + if (hw) { + return hw; + } + + return glue (audio_pcm_hw_find_any_, TYPE) (s, NULL); +} + +static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( + AudioState *s, + const char *sw_name, + audsettings_t *as, + int sw_endian + ) +{ + SW *sw; + HW *hw; + audsettings_t hw_as; + + if (glue (conf.fixed_, TYPE).enabled) { + hw_as = glue (conf.fixed_, TYPE).settings; + } + else { + hw_as = *as; + } + + sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw)); + if (!sw) { + dolog ("Could not allocate soft voice `%s' (%zu bytes)\n", + sw_name ? sw_name : "unknown", sizeof (*sw)); + goto err1; + } + + hw = glue (audio_pcm_hw_add_, TYPE) (s, &hw_as); + if (!hw) { + goto err2; + } + + glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw); + + if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as, sw_endian)) { + goto err3; + } + + return sw; + +err3: + glue (audio_pcm_hw_del_sw_, TYPE) (sw); + glue (audio_pcm_hw_gc_, TYPE) (s, &hw); +err2: + qemu_free (sw); +err1: + return NULL; +} + +static void glue (audio_close_, TYPE) (AudioState *s, SW *sw) +{ + glue (audio_pcm_sw_fini_, TYPE) (sw); + glue (audio_pcm_hw_del_sw_, TYPE) (sw); + glue (audio_pcm_hw_gc_, TYPE) (s, &sw->hw); + qemu_free (sw); +} + +void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw) +{ + if (sw) { + if (audio_bug (AUDIO_FUNC, !card || !card->audio)) { + dolog ("card=%p card->audio=%p\n", + card, card ? card->audio : NULL); + return; + } + + glue (audio_close_, TYPE) (card->audio, sw); + } +} + +SW *glue (AUD_open_, TYPE) ( + QEMUSoundCard *card, + SW *sw, + const char *name, + void *callback_opaque , + audio_callback_fn_t callback_fn, + audsettings_t *as, + int sw_endian + ) +{ + AudioState *s; +#ifdef DAC + int live = 0; + SW *old_sw = NULL; +#endif + + ldebug ("open %s, freq %d, nchannels %d, fmt %d\n", + name, as->freq, as->nchannels, as->fmt); + + if (audio_bug (AUDIO_FUNC, + !card || !card->audio || !name || !callback_fn || !as)) { + dolog ("card=%p card->audio=%p name=%p callback_fn=%p as=%p\n", + card, card ? card->audio : NULL, name, callback_fn, as); + goto fail; + } + + s = card->audio; + + if (audio_bug (AUDIO_FUNC, audio_validate_settigs (as))) { + audio_print_settings (as); + goto fail; + } + + if (audio_bug (AUDIO_FUNC, !s->drv)) { + dolog ("Can not open `%s' (no host audio driver)\n", name); + goto fail; + } + + if (sw && audio_pcm_info_eq (&sw->info, as)) { + return sw; + } + +#ifdef DAC + if (conf.plive && sw && (!sw->active && !sw->empty)) { + live = sw->total_hw_samples_mixed; + +#ifdef DEBUG_PLIVE + dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live); + dolog ("Old %s freq %d, bits %d, channels %d\n", + SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels); + dolog ("New %s freq %d, bits %d, channels %d\n", + name, + freq, + (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8, + nchannels); +#endif + + if (live) { + old_sw = sw; + old_sw->callback.fn = NULL; + sw = NULL; + } + } +#endif + + if (!glue (conf.fixed_, TYPE).enabled && sw) { + glue (AUD_close_, TYPE) (card, sw); + sw = NULL; + } + + if (sw) { + HW *hw = sw->hw; + + if (!hw) { + dolog ("Internal logic error voice `%s' has no hardware store\n", + SW_NAME (sw)); + goto fail; + } + + glue (audio_pcm_sw_fini_, TYPE) (sw); + if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as, sw_endian)) { + goto fail; + } + } + else { + sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, name, as, sw_endian); + if (!sw) { + dolog ("Failed to create voice `%s'\n", name); + return NULL; + } + } + + if (sw) { + sw->vol = nominal_volume; + sw->callback.fn = callback_fn; + sw->callback.opaque = callback_opaque; + +#ifdef DAC + if (live) { + int mixed = + (live << old_sw->info.shift) + * old_sw->info.bytes_per_second + / sw->info.bytes_per_second; + +#ifdef DEBUG_PLIVE + dolog ("Silence will be mixed %d\n", mixed); +#endif + sw->total_hw_samples_mixed += mixed; + } +#endif + +#ifdef DEBUG_AUDIO + dolog ("%s\n", name); + audio_pcm_print_info ("hw", &sw->hw->info); + audio_pcm_print_info ("sw", &sw->info); +#endif + } + + return sw; + + fail: + glue (AUD_close_, TYPE) (card, sw); + return NULL; +} + +int glue (AUD_is_active_, TYPE) (SW *sw) +{ + return sw ? sw->active : 0; +} + +void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) +{ + if (!sw) { + return; + } + + ts->old_ts = sw->hw->ts_helper; +} + +uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) +{ + uint64_t delta, cur_ts, old_ts; + + if (!sw) { + return 0; + } + + cur_ts = sw->hw->ts_helper; + old_ts = ts->old_ts; + /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */ + + if (cur_ts >= old_ts) { + delta = cur_ts - old_ts; + } + else { + delta = UINT64_MAX - old_ts + cur_ts; + } + + if (!delta) { + return 0; + } + + return (delta * sw->hw->info.freq) / 1000000; +} + +#undef TYPE +#undef HW +#undef SW +#undef HWBUF +#undef NAME diff --git a/tools/ioemu/audio/coreaudio.c b/tools/ioemu/audio/coreaudio.c new file mode 100644 index 0000000000..534fb3ef7c --- /dev/null +++ b/tools/ioemu/audio/coreaudio.c @@ -0,0 +1,564 @@ +/* + * QEMU OS X CoreAudio audio driver + * + * Copyright (c) 2005 Mike Kronenberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include /* strerror */ +#include /* pthread_X */ + +#include "vl.h" + +#define AUDIO_CAP "coreaudio" +#include "audio_int.h" + +struct { + int buffer_frames; + int nbuffers; + int isAtexit; +} conf = { + .buffer_frames = 512, + .nbuffers = 4, + .isAtexit = 0 +}; + +typedef struct coreaudioVoiceOut { + HWVoiceOut hw; + pthread_mutex_t mutex; + int isAtexit; + AudioDeviceID outputDeviceID; + UInt32 audioDevicePropertyBufferFrameSize; + AudioStreamBasicDescription outputStreamBasicDescription; + int live; + int decr; + int rpos; +} coreaudioVoiceOut; + +static void coreaudio_logstatus (OSStatus status) +{ + char *str = "BUG"; + + switch(status) { + case kAudioHardwareNoError: + str = "kAudioHardwareNoError"; + break; + + case kAudioHardwareNotRunningError: + str = "kAudioHardwareNotRunningError"; + break; + + case kAudioHardwareUnspecifiedError: + str = "kAudioHardwareUnspecifiedError"; + break; + + case kAudioHardwareUnknownPropertyError: + str = "kAudioHardwareUnknownPropertyError"; + break; + + case kAudioHardwareBadPropertySizeError: + str = "kAudioHardwareBadPropertySizeError"; + break; + + case kAudioHardwareIllegalOperationError: + str = "kAudioHardwareIllegalOperationError"; + break; + + case kAudioHardwareBadDeviceError: + str = "kAudioHardwareBadDeviceError"; + break; + + case kAudioHardwareBadStreamError: + str = "kAudioHardwareBadStreamError"; + break; + + case kAudioHardwareUnsupportedOperationError: + str = "kAudioHardwareUnsupportedOperationError"; + break; + + case kAudioDeviceUnsupportedFormatError: + str = "kAudioDeviceUnsupportedFormatError"; + break; + + case kAudioDevicePermissionsError: + str = "kAudioDevicePermissionsError"; + break; + + default: + AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status); + return; + } + + AUD_log (AUDIO_CAP, "Reason: %s\n", str); +} + +static void GCC_FMT_ATTR (2, 3) coreaudio_logerr ( + OSStatus status, + const char *fmt, + ... + ) +{ + va_list ap; + + va_start (ap, fmt); + AUD_log (AUDIO_CAP, fmt, ap); + va_end (ap); + + coreaudio_logstatus (status); +} + +static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( + OSStatus status, + const char *typ, + const char *fmt, + ... + ) +{ + va_list ap; + + AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + coreaudio_logstatus (status); +} + +static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) +{ + OSStatus status; + UInt32 result = 0; + UInt32 propertySize = sizeof(outputDeviceID); + status = AudioDeviceGetProperty( + outputDeviceID, 0, 0, + kAudioDevicePropertyDeviceIsRunning, &propertySize, &result); + if (status != kAudioHardwareNoError) { + coreaudio_logerr(status, + "Could not determine whether Device is playing\n"); + } + return result; +} + +static void coreaudio_atexit (void) +{ + conf.isAtexit = 1; +} + +static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name) +{ + int err; + + err = pthread_mutex_lock (&core->mutex); + if (err) { + dolog ("Could not lock voice for %s\nReason: %s\n", + fn_name, strerror (err)); + return -1; + } + return 0; +} + +static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name) +{ + int err; + + err = pthread_mutex_unlock (&core->mutex); + if (err) { + dolog ("Could not unlock voice for %s\nReason: %s\n", + fn_name, strerror (err)); + return -1; + } + return 0; +} + +static int coreaudio_run_out (HWVoiceOut *hw) +{ + int live, decr; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + + if (coreaudio_lock (core, "coreaudio_run_out")) { + return 0; + } + + live = audio_pcm_hw_get_live_out (hw); + + if (core->decr > live) { + ldebug ("core->decr %d live %d core->live %d\n", + core->decr, + live, + core->live); + } + + decr = audio_MIN (core->decr, live); + core->decr -= decr; + + core->live = live - decr; + hw->rpos = core->rpos; + + coreaudio_unlock (core, "coreaudio_run_out"); + return decr; +} + +/* callback to feed audiooutput buffer */ +static OSStatus audioDeviceIOProc( + AudioDeviceID inDevice, + const AudioTimeStamp* inNow, + const AudioBufferList* inInputData, + const AudioTimeStamp* inInputTime, + AudioBufferList* outOutputData, + const AudioTimeStamp* inOutputTime, + void* hwptr) +{ + UInt32 frame, frameCount; + float *out = outOutputData->mBuffers[0].mData; + HWVoiceOut *hw = hwptr; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; + int rpos, live; + st_sample_t *src; +#ifndef FLOAT_MIXENG +#ifdef RECIPROCAL + const float scale = 1.f / UINT_MAX; +#else + const float scale = UINT_MAX; +#endif +#endif + + if (coreaudio_lock (core, "audioDeviceIOProc")) { + inInputTime = 0; + return 0; + } + + frameCount = core->audioDevicePropertyBufferFrameSize; + live = core->live; + + /* if there are not enough samples, set signal and return */ + if (live < frameCount) { + inInputTime = 0; + coreaudio_unlock (core, "audioDeviceIOProc(empty)"); + return 0; + } + + rpos = core->rpos; + src = hw->mix_buf + rpos; + + /* fill buffer */ + for (frame = 0; frame < frameCount; frame++) { +#ifdef FLOAT_MIXENG + *out++ = src[frame].l; /* left channel */ + *out++ = src[frame].r; /* right channel */ +#else +#ifdef RECIPROCAL + *out++ = src[frame].l * scale; /* left channel */ + *out++ = src[frame].r * scale; /* right channel */ +#else + *out++ = src[frame].l / scale; /* left channel */ + *out++ = src[frame].r / scale; /* right channel */ +#endif +#endif + } + + /* cleanup */ + mixeng_clear (src, frameCount); + rpos = (rpos + frameCount) % hw->samples; + core->decr += frameCount; + core->rpos = rpos; + + coreaudio_unlock (core, "audioDeviceIOProc"); + return 0; +} + +static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as) +{ + OSStatus status; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + UInt32 propertySize; + int err; + int bits = 8; + int endianess = 0; + const char *typ = "playback"; + AudioValueRange frameRange; + + /* create mutex */ + err = pthread_mutex_init(&core->mutex, NULL); + if (err) { + dolog("Could not create mutex\nReason: %s\n", strerror (err)); + return -1; + } + + if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) { + bits = 16; + endianess = 1; + } + + audio_pcm_init_info ( + &hw->info, + as, + /* Following is irrelevant actually since we do not use + mixengs clipping routines */ + audio_need_to_swap_endian (endianess) + ); + + /* open default output device */ + propertySize = sizeof(core->outputDeviceID); + status = AudioHardwareGetProperty( + kAudioHardwarePropertyDefaultOutputDevice, + &propertySize, + &core->outputDeviceID); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, + "Could not get default output Device\n"); + return -1; + } + if (core->outputDeviceID == kAudioDeviceUnknown) { + dolog ("Could not initialize %s - Unknown Audiodevice\n", typ); + return -1; + } + + /* get minimum and maximum buffer frame sizes */ + propertySize = sizeof(frameRange); + status = AudioDeviceGetProperty( + core->outputDeviceID, + 0, + 0, + kAudioDevicePropertyBufferFrameSizeRange, + &propertySize, + &frameRange); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, + "Could not get device buffer frame range\n"); + return -1; + } + + if (frameRange.mMinimum > conf.buffer_frames) { + core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; + dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); + } + else if (frameRange.mMaximum < conf.buffer_frames) { + core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; + dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); + } + else { + core->audioDevicePropertyBufferFrameSize = conf.buffer_frames; + } + + /* set Buffer Frame Size */ + propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); + status = AudioDeviceSetProperty( + core->outputDeviceID, + NULL, + 0, + false, + kAudioDevicePropertyBufferFrameSize, + propertySize, + &core->audioDevicePropertyBufferFrameSize); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, + "Could not set device buffer frame size %ld\n", + core->audioDevicePropertyBufferFrameSize); + return -1; + } + + /* get Buffer Frame Size */ + propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); + status = AudioDeviceGetProperty( + core->outputDeviceID, + 0, + false, + kAudioDevicePropertyBufferFrameSize, + &propertySize, + &core->audioDevicePropertyBufferFrameSize); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, + "Could not get device buffer frame size\n"); + return -1; + } + hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize; + + /* get StreamFormat */ + propertySize = sizeof(core->outputStreamBasicDescription); + status = AudioDeviceGetProperty( + core->outputDeviceID, + 0, + false, + kAudioDevicePropertyStreamFormat, + &propertySize, + &core->outputStreamBasicDescription); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, + "Could not get Device Stream properties\n"); + core->outputDeviceID = kAudioDeviceUnknown; + return -1; + } + + /* set Samplerate */ + core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; + propertySize = sizeof(core->outputStreamBasicDescription); + status = AudioDeviceSetProperty( + core->outputDeviceID, + 0, + 0, + 0, + kAudioDevicePropertyStreamFormat, + propertySize, + &core->outputStreamBasicDescription); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", + as->freq); + core->outputDeviceID = kAudioDeviceUnknown; + return -1; + } + + /* set Callback */ + status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, "Could not set IOProc\n"); + core->outputDeviceID = kAudioDeviceUnknown; + return -1; + } + + /* start Playback */ + if (!isPlaying(core->outputDeviceID)) { + status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); + if (status != kAudioHardwareNoError) { + coreaudio_logerr2 (status, typ, "Could not start playback\n"); + AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc); + core->outputDeviceID = kAudioDeviceUnknown; + return -1; + } + } + + return 0; +} + +static void coreaudio_fini_out (HWVoiceOut *hw) +{ + OSStatus status; + int err; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + + if (!conf.isAtexit) { + /* stop playback */ + if (isPlaying(core->outputDeviceID)) { + status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); + if (status != kAudioHardwareNoError) { + coreaudio_logerr (status, "Could not stop playback\n"); + } + } + + /* remove callback */ + status = AudioDeviceRemoveIOProc(core->outputDeviceID, + audioDeviceIOProc); + if (status != kAudioHardwareNoError) { + coreaudio_logerr (status, "Could not remove IOProc\n"); + } + } + core->outputDeviceID = kAudioDeviceUnknown; + + /* destroy mutex */ + err = pthread_mutex_destroy(&core->mutex); + if (err) { + dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); + } +} + +static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + OSStatus status; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + + switch (cmd) { + case VOICE_ENABLE: + /* start playback */ + if (!isPlaying(core->outputDeviceID)) { + status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); + if (status != kAudioHardwareNoError) { + coreaudio_logerr (status, "Could not resume playback\n"); + } + } + break; + + case VOICE_DISABLE: + /* stop playback */ + if (!conf.isAtexit) { + if (isPlaying(core->outputDeviceID)) { + status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); + if (status != kAudioHardwareNoError) { + coreaudio_logerr (status, "Could not pause playback\n"); + } + } + } + break; + } + return 0; +} + +static void *coreaudio_audio_init (void) +{ + atexit(coreaudio_atexit); + return &coreaudio_audio_init; +} + +static void coreaudio_audio_fini (void *opaque) +{ + (void) opaque; +} + +static struct audio_option coreaudio_options[] = { + {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames, + "Size of the buffer in frames", NULL, 0}, + {"BUFFER_COUNT", AUD_OPT_INT, &conf.nbuffers, + "Number of buffers", NULL, 0}, + {NULL, 0, NULL, NULL, NULL, 0} +}; + +static struct audio_pcm_ops coreaudio_pcm_ops = { + coreaudio_init_out, + coreaudio_fini_out, + coreaudio_run_out, + coreaudio_write, + coreaudio_ctl_out, + + NULL, + NULL, + NULL, + NULL, + NULL +}; + +struct audio_driver coreaudio_audio_driver = { + INIT_FIELD (name = ) "coreaudio", + INIT_FIELD (descr = ) + "CoreAudio http://developer.apple.com/audio/coreaudio.html", + INIT_FIELD (options = ) coreaudio_options, + INIT_FIELD (init = ) coreaudio_audio_init, + INIT_FIELD (fini = ) coreaudio_audio_fini, + INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops, + INIT_FIELD (can_be_default = ) 1, + INIT_FIELD (max_voices_out = ) 1, + INIT_FIELD (max_voices_in = ) 0, + INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut), + INIT_FIELD (voice_size_in = ) 0 +}; diff --git a/tools/ioemu/audio/dsound_template.h b/tools/ioemu/audio/dsound_template.h new file mode 100644 index 0000000000..38ba5b9ca0 --- /dev/null +++ b/tools/ioemu/audio/dsound_template.h @@ -0,0 +1,282 @@ +/* + * QEMU DirectSound audio driver header + * + * Copyright (c) 2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifdef DSBTYPE_IN +#define NAME "capture buffer" +#define TYPE in +#define IFACE IDirectSoundCaptureBuffer +#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER +#define FIELD dsound_capture_buffer +#else +#define NAME "playback buffer" +#define TYPE out +#define IFACE IDirectSoundBuffer +#define BUFPTR LPDIRECTSOUNDBUFFER +#define FIELD dsound_buffer +#endif + +static int glue (dsound_unlock_, TYPE) ( + BUFPTR buf, + LPVOID p1, + LPVOID p2, + DWORD blen1, + DWORD blen2 + ) +{ + HRESULT hr; + + hr = glue (IFACE, _Unlock) (buf, p1, blen1, p2, blen2); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not unlock " NAME "\n"); + return -1; + } + + return 0; +} + +static int glue (dsound_lock_, TYPE) ( + BUFPTR buf, + struct audio_pcm_info *info, + DWORD pos, + DWORD len, + LPVOID *p1p, + LPVOID *p2p, + DWORD *blen1p, + DWORD *blen2p, + int entire + ) +{ + HRESULT hr; + int i; + LPVOID p1 = NULL, p2 = NULL; + DWORD blen1 = 0, blen2 = 0; + + for (i = 0; i < conf.lock_retries; ++i) { + hr = glue (IFACE, _Lock) ( + buf, + pos, + len, + &p1, + &blen1, + &p2, + &blen2, + (entire +#ifdef DSBTYPE_IN + ? DSCBLOCK_ENTIREBUFFER +#else + ? DSBLOCK_ENTIREBUFFER +#endif + : 0) + ); + + if (FAILED (hr)) { +#ifndef DSBTYPE_IN + if (hr == DSERR_BUFFERLOST) { + if (glue (dsound_restore_, TYPE) (buf)) { + dsound_logerr (hr, "Could not lock " NAME "\n"); + goto fail; + } + continue; + } +#endif + dsound_logerr (hr, "Could not lock " NAME "\n"); + goto fail; + } + + break; + } + + if (i == conf.lock_retries) { + dolog ("%d attempts to lock " NAME " failed\n", i); + goto fail; + } + + if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) { + dolog ("DirectSound returned misaligned buffer %ld %ld\n", + blen1, blen2); + glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2); + goto fail; + } + + if (!p1 && blen1) { + dolog ("warning: !p1 && blen1=%ld\n", blen1); + blen1 = 0; + } + + if (!p2 && blen2) { + dolog ("warning: !p2 && blen2=%ld\n", blen2); + blen2 = 0; + } + + *p1p = p1; + *p2p = p2; + *blen1p = blen1; + *blen2p = blen2; + return 0; + + fail: + *p1p = NULL - 1; + *p2p = NULL - 1; + *blen1p = -1; + *blen2p = -1; + return -1; +} + +#ifdef DSBTYPE_IN +static void dsound_fini_in (HWVoiceIn *hw) +#else +static void dsound_fini_out (HWVoiceOut *hw) +#endif +{ + HRESULT hr; +#ifdef DSBTYPE_IN + DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; +#else + DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; +#endif + + if (ds->FIELD) { + hr = glue (IFACE, _Stop) (ds->FIELD); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not stop " NAME "\n"); + } + + hr = glue (IFACE, _Release) (ds->FIELD); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not release " NAME "\n"); + } + ds->FIELD = NULL; + } +} + +#ifdef DSBTYPE_IN +static int dsound_init_in (HWVoiceIn *hw, audsettings_t *as) +#else +static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as) +#endif +{ + int err; + HRESULT hr; + dsound *s = &glob_dsound; + WAVEFORMATEX wfx; + audsettings_t obt_as; +#ifdef DSBTYPE_IN + const char *typ = "ADC"; + DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; + DSCBUFFERDESC bd; + DSCBCAPS bc; +#else + const char *typ = "DAC"; + DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; + DSBUFFERDESC bd; + DSBCAPS bc; +#endif + + err = waveformat_from_audio_settings (&wfx, as); + if (err) { + return -1; + } + + memset (&bd, 0, sizeof (bd)); + bd.dwSize = sizeof (bd); + bd.lpwfxFormat = &wfx; +#ifdef DSBTYPE_IN + bd.dwBufferBytes = conf.bufsize_in; + hr = IDirectSoundCapture_CreateCaptureBuffer ( + s->dsound_capture, + &bd, + &ds->dsound_capture_buffer, + NULL + ); +#else + bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; + bd.dwBufferBytes = conf.bufsize_out; + hr = IDirectSound_CreateSoundBuffer ( + s->dsound, + &bd, + &ds->dsound_buffer, + NULL + ); +#endif + + if (FAILED (hr)) { + dsound_logerr2 (hr, typ, "Could not create " NAME "\n"); + return -1; + } + + hr = glue (IFACE, _GetFormat) (ds->FIELD, &wfx, sizeof (wfx), NULL); + if (FAILED (hr)) { + dsound_logerr2 (hr, typ, "Could not get " NAME " format\n"); + goto fail0; + } + +#ifdef DEBUG_DSOUND + dolog (NAME "\n"); + print_wave_format (&wfx); +#endif + + memset (&bc, 0, sizeof (bc)); + bc.dwSize = sizeof (bc); + + hr = glue (IFACE, _GetCaps) (ds->FIELD, &bc); + if (FAILED (hr)) { + dsound_logerr2 (hr, typ, "Could not get " NAME " format\n"); + goto fail0; + } + + err = waveformat_to_audio_settings (&wfx, &obt_as); + if (err) { + goto fail0; + } + + ds->first_time = 1; + + audio_pcm_init_info (&hw->info, &obt_as, audio_need_to_swap_endian (0)); + + if (bc.dwBufferBytes & hw->info.align) { + dolog ( + "GetCaps returned misaligned buffer size %ld, alignment %d\n", + bc.dwBufferBytes, hw->info.align + 1 + ); + } + hw->samples = bc.dwBufferBytes >> hw->info.shift; + +#ifdef DEBUG_DSOUND + dolog ("caps %ld, desc %ld\n", + bc.dwBufferBytes, bd.dwBufferBytes); + + dolog ("bufsize %d, freq %d, chan %d, fmt %d\n", + hw->bufsize, settings.freq, settings.nchannels, settings.fmt); +#endif + return 0; + + fail0: + glue (dsound_fini_, TYPE) (hw); + return -1; +} + +#undef NAME +#undef TYPE +#undef IFACE +#undef BUFPTR +#undef FIELD diff --git a/tools/ioemu/audio/dsoundaudio.c b/tools/ioemu/audio/dsoundaudio.c new file mode 100644 index 0000000000..63c5a50573 --- /dev/null +++ b/tools/ioemu/audio/dsoundaudio.c @@ -0,0 +1,1076 @@ +/* + * QEMU DirectSound audio driver + * + * Copyright (c) 2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation + */ + +#include "vl.h" + +#define AUDIO_CAP "dsound" +#include "audio_int.h" + +#include +#include +#include + +/* #define DEBUG_DSOUND */ + +static struct { + int lock_retries; + int restore_retries; + int getstatus_retries; + int set_primary; + int bufsize_in; + int bufsize_out; + audsettings_t settings; + int latency_millis; +} conf = { + 1, + 1, + 1, + 0, + 16384, + 16384, + { + 44100, + 2, + AUD_FMT_S16 + }, + 10 +}; + +typedef struct { + LPDIRECTSOUND dsound; + LPDIRECTSOUNDCAPTURE dsound_capture; + LPDIRECTSOUNDBUFFER dsound_primary_buffer; + audsettings_t settings; +} dsound; + +static dsound glob_dsound; + +typedef struct { + HWVoiceOut hw; + LPDIRECTSOUNDBUFFER dsound_buffer; + DWORD old_pos; + int first_time; +#ifdef DEBUG_DSOUND + DWORD old_ppos; + DWORD played; + DWORD mixed; +#endif +} DSoundVoiceOut; + +typedef struct { + HWVoiceIn hw; + int first_time; + LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; +} DSoundVoiceIn; + +static void dsound_log_hresult (HRESULT hr) +{ + const char *str = "BUG"; + + switch (hr) { + case DS_OK: + str = "The method succeeded"; + break; +#ifdef DS_NO_VIRTUALIZATION + case DS_NO_VIRTUALIZATION: + str = "The buffer was created, but another 3D algorithm was substituted"; + break; +#endif +#ifdef DS_INCOMPLETE + case DS_INCOMPLETE: + str = "The method succeeded, but not all the optional effects were obtained"; + break; +#endif +#ifdef DSERR_ACCESSDENIED + case DSERR_ACCESSDENIED: + str = "The request failed because access was denied"; + break; +#endif +#ifdef DSERR_ALLOCATED + case DSERR_ALLOCATED: + str = "The request failed because resources, such as a priority level, were already in use by another caller"; + break; +#endif +#ifdef DSERR_ALREADYINITIALIZED + case DSERR_ALREADYINITIALIZED: + str = "The object is already initialized"; + break; +#endif +#ifdef DSERR_BADFORMAT + case DSERR_BADFORMAT: + str = "The specified wave format is not supported"; + break; +#endif +#ifdef DSERR_BADSENDBUFFERGUID + case DSERR_BADSENDBUFFERGUID: + str = "The GUID specified in an audiopath file does not match a valid mix-in buffer"; + break; +#endif +#ifdef DSERR_BUFFERLOST + case DSERR_BUFFERLOST: + str = "The buffer memory has been lost and must be restored"; + break; +#endif +#ifdef DSERR_BUFFERTOOSMALL + case DSERR_BUFFERTOOSMALL: + str = "The buffer size is not great enough to enable effects processing"; + break; +#endif +#ifdef DSERR_CONTROLUNAVAIL + case DSERR_CONTROLUNAVAIL: + str = "The buffer control (volume, pan, and so on) requested by the caller is not available. Controls must be specified when the buffer is created, using the dwFlags member of DSBUFFERDESC"; + break; +#endif +#ifdef DSERR_DS8_REQUIRED + case DSERR_DS8_REQUIRED: + str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface"; + break; +#endif +#ifdef DSERR_FXUNAVAILABLE + case DSERR_FXUNAVAILABLE: + str = "The effects requested could not be found on the system, or they are in the wrong order or in the wrong location; for example, an effect expected in hardware was found in software"; + break; +#endif +#ifdef DSERR_GENERIC + case DSERR_GENERIC : + str = "An undetermined error occurred inside the DirectSound subsystem"; + break; +#endif +#ifdef DSERR_INVALIDCALL + case DSERR_INVALIDCALL: + str = "This function is not valid for the current state of this object"; + break; +#endif +#ifdef DSERR_INVALIDPARAM + case DSERR_INVALIDPARAM: + str = "An invalid parameter was passed to the returning function"; + break; +#endif +#ifdef DSERR_NOAGGREGATION + case DSERR_NOAGGREGATION: + str = "The object does not support aggregation"; + break; +#endif +#ifdef DSERR_NODRIVER + case DSERR_NODRIVER: + str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID"; + break; +#endif +#ifdef DSERR_NOINTERFACE + case DSERR_NOINTERFACE: + str = "The requested COM interface is not available"; + break; +#endif +#ifdef DSERR_OBJECTNOTFOUND + case DSERR_OBJECTNOTFOUND: + str = "The requested object was not found"; + break; +#endif +#ifdef DSERR_OTHERAPPHASPRIO + case DSERR_OTHERAPPHASPRIO: + str = "Another application has a higher priority level, preventing this call from succeeding"; + break; +#endif +#ifdef DSERR_OUTOFMEMORY + case DSERR_OUTOFMEMORY: + str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request"; + break; +#endif +#ifdef DSERR_PRIOLEVELNEEDED + case DSERR_PRIOLEVELNEEDED: + str = "A cooperative level of DSSCL_PRIORITY or higher is required"; + break; +#endif +#ifdef DSERR_SENDLOOP + case DSERR_SENDLOOP: + str = "A circular loop of send effects was detected"; + break; +#endif +#ifdef DSERR_UNINITIALIZED + case DSERR_UNINITIALIZED: + str = "The Initialize method has not been called or has not been called successfully before other methods were called"; + break; +#endif +#ifdef DSERR_UNSUPPORTED + case DSERR_UNSUPPORTED: + str = "The function called is not supported at this time"; + break; +#endif + default: + AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr); + return; + } + + AUD_log (AUDIO_CAP, "Reason: %s\n", str); +} + +static void GCC_FMT_ATTR (2, 3) dsound_logerr ( + HRESULT hr, + const char *fmt, + ... + ) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + dsound_log_hresult (hr); +} + +static void GCC_FMT_ATTR (3, 4) dsound_logerr2 ( + HRESULT hr, + const char *typ, + const char *fmt, + ... + ) +{ + va_list ap; + + AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + dsound_log_hresult (hr); +} + +static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis) +{ + return (millis * info->bytes_per_second) / 1000; +} + +#ifdef DEBUG_DSOUND +static void print_wave_format (WAVEFORMATEX *wfx) +{ + dolog ("tag = %d\n", wfx->wFormatTag); + dolog ("nChannels = %d\n", wfx->nChannels); + dolog ("nSamplesPerSec = %ld\n", wfx->nSamplesPerSec); + dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec); + dolog ("nBlockAlign = %d\n", wfx->nBlockAlign); + dolog ("wBitsPerSample = %d\n", wfx->wBitsPerSample); + dolog ("cbSize = %d\n", wfx->cbSize); +} +#endif + +static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb) +{ + HRESULT hr; + int i; + + for (i = 0; i < conf.restore_retries; ++i) { + hr = IDirectSoundBuffer_Restore (dsb); + + switch (hr) { + case DS_OK: + return 0; + + case DSERR_BUFFERLOST: + continue; + + default: + dsound_logerr (hr, "Could not restore playback buffer\n"); + return -1; + } + } + + dolog ("%d attempts to restore playback buffer failed\n", i); + return -1; +} + +static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as) +{ + memset (wfx, 0, sizeof (*wfx)); + + wfx->wFormatTag = WAVE_FORMAT_PCM; + wfx->nChannels = as->nchannels; + wfx->nSamplesPerSec = as->freq; + wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2); + wfx->nBlockAlign = 1 << (as->nchannels == 2); + wfx->cbSize = 0; + + switch (as->fmt) { + case AUD_FMT_S8: + wfx->wBitsPerSample = 8; + break; + + case AUD_FMT_U8: + wfx->wBitsPerSample = 8; + break; + + case AUD_FMT_S16: + wfx->wBitsPerSample = 16; + wfx->nAvgBytesPerSec <<= 1; + wfx->nBlockAlign <<= 1; + break; + + case AUD_FMT_U16: + wfx->wBitsPerSample = 16; + wfx->nAvgBytesPerSec <<= 1; + wfx->nBlockAlign <<= 1; + break; + + default: + dolog ("Internal logic error: Bad audio format %d\n", as->freq); + return -1; + } + + return 0; +} + +static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as) +{ + if (wfx->wFormatTag != WAVE_FORMAT_PCM) { + dolog ("Invalid wave format, tag is not PCM, but %d\n", + wfx->wFormatTag); + return -1; + } + + if (!wfx->nSamplesPerSec) { + dolog ("Invalid wave format, frequency is zero\n"); + return -1; + } + as->freq = wfx->nSamplesPerSec; + + switch (wfx->nChannels) { + case 1: + as->nchannels = 1; + break; + + case 2: + as->nchannels = 2; + break; + + default: + dolog ( + "Invalid wave format, number of channels is not 1 or 2, but %d\n", + wfx->nChannels + ); + return -1; + } + + switch (wfx->wBitsPerSample) { + case 8: + as->fmt = AUD_FMT_U8; + break; + + case 16: + as->fmt = AUD_FMT_S16; + break; + + default: + dolog ("Invalid wave format, bits per sample is not 8 or 16, but %d\n", + wfx->wBitsPerSample); + return -1; + } + + return 0; +} + +#include "dsound_template.h" +#define DSBTYPE_IN +#include "dsound_template.h" +#undef DSBTYPE_IN + +static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp) +{ + HRESULT hr; + int i; + + for (i = 0; i < conf.getstatus_retries; ++i) { + hr = IDirectSoundBuffer_GetStatus (dsb, statusp); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not get playback buffer status\n"); + return -1; + } + + if (*statusp & DSERR_BUFFERLOST) { + if (dsound_restore_out (dsb)) { + return -1; + } + continue; + } + break; + } + + return 0; +} + +static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb, + DWORD *statusp) +{ + HRESULT hr; + + hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not get capture buffer status\n"); + return -1; + } + + return 0; +} + +static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) +{ + int src_len1 = dst_len; + int src_len2 = 0; + int pos = hw->rpos + dst_len; + st_sample_t *src1 = hw->mix_buf + hw->rpos; + st_sample_t *src2 = NULL; + + if (pos > hw->samples) { + src_len1 = hw->samples - hw->rpos; + src2 = hw->mix_buf; + src_len2 = dst_len - src_len1; + pos = src_len2; + } + + if (src_len1) { + hw->clip (dst, src1, src_len1); + mixeng_clear (src1, src_len1); + } + + if (src_len2) { + dst = advance (dst, src_len1 << hw->info.shift); + hw->clip (dst, src2, src_len2); + mixeng_clear (src2, src_len2); + } + + hw->rpos = pos % hw->samples; +} + +static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb) +{ + int err; + LPVOID p1, p2; + DWORD blen1, blen2, len1, len2; + + err = dsound_lock_out ( + dsb, + &hw->info, + 0, + hw->samples << hw->info.shift, + &p1, &p2, + &blen1, &blen2, + 1 + ); + if (err) { + return; + } + + len1 = blen1 >> hw->info.shift; + len2 = blen2 >> hw->info.shift; + +#ifdef DEBUG_DSOUND + dolog ("clear %p,%ld,%ld %p,%ld,%ld\n", + p1, blen1, len1, + p2, blen2, len2); +#endif + + if (p1 && len1) { + audio_pcm_info_clear_buf (&hw->info, p1, len1); + } + + if (p2 && len2) { + audio_pcm_info_clear_buf (&hw->info, p2, len2); + } + + dsound_unlock_out (dsb, p1, p2, blen1, blen2); +} + +static void dsound_close (dsound *s) +{ + HRESULT hr; + + if (s->dsound_primary_buffer) { + hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not release primary buffer\n"); + } + s->dsound_primary_buffer = NULL; + } +} + +static int dsound_open (dsound *s) +{ + int err; + HRESULT hr; + WAVEFORMATEX wfx; + DSBUFFERDESC dsbd; + HWND hwnd; + + hwnd = GetForegroundWindow (); + hr = IDirectSound_SetCooperativeLevel ( + s->dsound, + hwnd, + DSSCL_PRIORITY + ); + + if (FAILED (hr)) { + dsound_logerr (hr, "Could not set cooperative level for window %p\n", + hwnd); + return -1; + } + + if (!conf.set_primary) { + return 0; + } + + err = waveformat_from_audio_settings (&wfx, &conf.settings); + if (err) { + return -1; + } + + memset (&dsbd, 0, sizeof (dsbd)); + dsbd.dwSize = sizeof (dsbd); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbd.dwBufferBytes = 0; + dsbd.lpwfxFormat = NULL; + + hr = IDirectSound_CreateSoundBuffer ( + s->dsound, + &dsbd, + &s->dsound_primary_buffer, + NULL + ); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not create primary playback buffer\n"); + return -1; + } + + hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not set primary playback buffer format\n"); + } + + hr = IDirectSoundBuffer_GetFormat ( + s->dsound_primary_buffer, + &wfx, + sizeof (wfx), + NULL + ); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not get primary playback buffer format\n"); + goto fail0; + } + +#ifdef DEBUG_DSOUND + dolog ("Primary\n"); + print_wave_format (&wfx); +#endif + + err = waveformat_to_audio_settings (&wfx, &s->settings); + if (err) { + goto fail0; + } + + return 0; + + fail0: + dsound_close (s); + return -1; +} + +static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + HRESULT hr; + DWORD status; + DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; + LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; + + if (!dsb) { + dolog ("Attempt to control voice without a buffer\n"); + return 0; + } + + switch (cmd) { + case VOICE_ENABLE: + if (dsound_get_status_out (dsb, &status)) { + return -1; + } + + if (status & DSBSTATUS_PLAYING) { + dolog ("warning: Voice is already playing\n"); + return 0; + } + + dsound_clear_sample (hw, dsb); + + hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not start playing buffer\n"); + return -1; + } + break; + + case VOICE_DISABLE: + if (dsound_get_status_out (dsb, &status)) { + return -1; + } + + if (status & DSBSTATUS_PLAYING) { + hr = IDirectSoundBuffer_Stop (dsb); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not stop playing buffer\n"); + return -1; + } + } + else { + dolog ("warning: Voice is not playing\n"); + } + break; + } + return 0; +} + +static int dsound_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int dsound_run_out (HWVoiceOut *hw) +{ + int err; + HRESULT hr; + DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; + LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; + int live, len, hwshift; + DWORD blen1, blen2; + DWORD len1, len2; + DWORD decr; + DWORD wpos, ppos, old_pos; + LPVOID p1, p2; + int bufsize; + + if (!dsb) { + dolog ("Attempt to run empty with playback buffer\n"); + return 0; + } + + hwshift = hw->info.shift; + bufsize = hw->samples << hwshift; + + live = audio_pcm_hw_get_live_out (hw); + + hr = IDirectSoundBuffer_GetCurrentPosition ( + dsb, + &ppos, + ds->first_time ? &wpos : NULL + ); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not get playback buffer position\n"); + return 0; + } + + len = live << hwshift; + + if (ds->first_time) { + if (conf.latency_millis) { + DWORD cur_blat; + + cur_blat = audio_ring_dist (wpos, ppos, bufsize); + ds->first_time = 0; + old_pos = wpos; + old_pos += + millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat; + old_pos %= bufsize; + old_pos &= ~hw->info.align; + } + else { + old_pos = wpos; + } +#ifdef DEBUG_DSOUND + ds->played = 0; + ds->mixed = 0; +#endif + } + else { + if (ds->old_pos == ppos) { +#ifdef DEBUG_DSOUND + dolog ("old_pos == ppos\n"); +#endif + return 0; + } + +#ifdef DEBUG_DSOUND + ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize); +#endif + old_pos = ds->old_pos; + } + + if ((old_pos < ppos) && ((old_pos + len) > ppos)) { + len = ppos - old_pos; + } + else { + if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) { + len = bufsize - old_pos + ppos; + } + } + + if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) { + dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n", + len, bufsize, old_pos, ppos); + return 0; + } + + len &= ~hw->info.align; + if (!len) { + return 0; + } + +#ifdef DEBUG_DSOUND + ds->old_ppos = ppos; +#endif + err = dsound_lock_out ( + dsb, + &hw->info, + old_pos, + len, + &p1, &p2, + &blen1, &blen2, + 0 + ); + if (err) { + return 0; + } + + len1 = blen1 >> hwshift; + len2 = blen2 >> hwshift; + decr = len1 + len2; + + if (p1 && len1) { + dsound_write_sample (hw, p1, len1); + } + + if (p2 && len2) { + dsound_write_sample (hw, p2, len2); + } + + dsound_unlock_out (dsb, p1, p2, blen1, blen2); + ds->old_pos = (old_pos + (decr << hwshift)) % bufsize; + +#ifdef DEBUG_DSOUND + ds->mixed += decr << hwshift; + + dolog ("played %lu mixed %lu diff %ld sec %f\n", + ds->played, + ds->mixed, + ds->mixed - ds->played, + abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second); +#endif + return decr; +} + +static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...) +{ + HRESULT hr; + DWORD status; + DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; + LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; + + if (!dscb) { + dolog ("Attempt to control capture voice without a buffer\n"); + return -1; + } + + switch (cmd) { + case VOICE_ENABLE: + if (dsound_get_status_in (dscb, &status)) { + return -1; + } + + if (status & DSCBSTATUS_CAPTURING) { + dolog ("warning: Voice is already capturing\n"); + return 0; + } + + /* clear ?? */ + + hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not start capturing\n"); + return -1; + } + break; + + case VOICE_DISABLE: + if (dsound_get_status_in (dscb, &status)) { + return -1; + } + + if (status & DSCBSTATUS_CAPTURING) { + hr = IDirectSoundCaptureBuffer_Stop (dscb); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not stop capturing\n"); + return -1; + } + } + else { + dolog ("warning: Voice is not capturing\n"); + } + break; + } + return 0; +} + +static int dsound_read (SWVoiceIn *sw, void *buf, int len) +{ + return audio_pcm_sw_read (sw, buf, len); +} + +static int dsound_run_in (HWVoiceIn *hw) +{ + int err; + HRESULT hr; + DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; + LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; + int live, len, dead; + DWORD blen1, blen2; + DWORD len1, len2; + DWORD decr; + DWORD cpos, rpos; + LPVOID p1, p2; + int hwshift; + + if (!dscb) { + dolog ("Attempt to run without capture buffer\n"); + return 0; + } + + hwshift = hw->info.shift; + + live = audio_pcm_hw_get_live_in (hw); + dead = hw->samples - live; + if (!dead) { + return 0; + } + + hr = IDirectSoundCaptureBuffer_GetCurrentPosition ( + dscb, + &cpos, + ds->first_time ? &rpos : NULL + ); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not get capture buffer position\n"); + return 0; + } + + if (ds->first_time) { + ds->first_time = 0; + if (rpos & hw->info.align) { + ldebug ("warning: Misaligned capture read position %ld(%d)\n", + rpos, hw->info.align); + } + hw->wpos = rpos >> hwshift; + } + + if (cpos & hw->info.align) { + ldebug ("warning: Misaligned capture position %ld(%d)\n", + cpos, hw->info.align); + } + cpos >>= hwshift; + + len = audio_ring_dist (cpos, hw->wpos, hw->samples); + if (!len) { + return 0; + } + len = audio_MIN (len, dead); + + err = dsound_lock_in ( + dscb, + &hw->info, + hw->wpos << hwshift, + len << hwshift, + &p1, + &p2, + &blen1, + &blen2, + 0 + ); + if (err) { + return 0; + } + + len1 = blen1 >> hwshift; + len2 = blen2 >> hwshift; + decr = len1 + len2; + + if (p1 && len1) { + hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); + } + + if (p2 && len2) { + hw->conv (hw->conv_buf, p2, len2, &nominal_volume); + } + + dsound_unlock_in (dscb, p1, p2, blen1, blen2); + hw->wpos = (hw->wpos + decr) % hw->samples; + return decr; +} + +static void dsound_audio_fini (void *opaque) +{ + HRESULT hr; + dsound *s = opaque; + + if (!s->dsound) { + return; + } + + hr = IDirectSound_Release (s->dsound); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not release DirectSound\n"); + } + s->dsound = NULL; + + if (!s->dsound_capture) { + return; + } + + hr = IDirectSoundCapture_Release (s->dsound_capture); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not release DirectSoundCapture\n"); + } + s->dsound_capture = NULL; +} + +static void *dsound_audio_init (void) +{ + int err; + HRESULT hr; + dsound *s = &glob_dsound; + + hr = CoInitialize (NULL); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not initialize COM\n"); + return NULL; + } + + hr = CoCreateInstance ( + &CLSID_DirectSound, + NULL, + CLSCTX_ALL, + &IID_IDirectSound, + (void **) &s->dsound + ); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not create DirectSound instance\n"); + return NULL; + } + + hr = IDirectSound_Initialize (s->dsound, NULL); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not initialize DirectSound\n"); + return NULL; + } + + hr = CoCreateInstance ( + &CLSID_DirectSoundCapture, + NULL, + CLSCTX_ALL, + &IID_IDirectSoundCapture, + (void **) &s->dsound_capture + ); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not create DirectSoundCapture instance\n"); + } + else { + hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not initialize DirectSoundCapture\n"); + + hr = IDirectSoundCapture_Release (s->dsound_capture); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not release DirectSoundCapture\n"); + } + s->dsound_capture = NULL; + } + } + + err = dsound_open (s); + if (err) { + dsound_audio_fini (s); + return NULL; + } + + return s; +} + +static struct audio_option dsound_options[] = { + {"LOCK_RETRIES", AUD_OPT_INT, &conf.lock_retries, + "Number of times to attempt locking the buffer", NULL, 0}, + {"RESTOURE_RETRIES", AUD_OPT_INT, &conf.restore_retries, + "Number of times to attempt restoring the buffer", NULL, 0}, + {"GETSTATUS_RETRIES", AUD_OPT_INT, &conf.getstatus_retries, + "Number of times to attempt getting status of the buffer", NULL, 0}, + {"SET_PRIMARY", AUD_OPT_BOOL, &conf.set_primary, + "Set the parameters of primary buffer", NULL, 0}, + {"LATENCY_MILLIS", AUD_OPT_INT, &conf.latency_millis, + "(undocumented)", NULL, 0}, + {"PRIMARY_FREQ", AUD_OPT_INT, &conf.settings.freq, + "Primary buffer frequency", NULL, 0}, + {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels, + "Primary buffer number of channels (1 - mono, 2 - stereo)", NULL, 0}, + {"PRIMARY_FMT", AUD_OPT_FMT, &conf.settings.fmt, + "Primary buffer format", NULL, 0}, + {"BUFSIZE_OUT", AUD_OPT_INT, &conf.bufsize_out, + "(undocumented)", NULL, 0}, + {"BUFSIZE_IN", AUD_OPT_INT, &conf.bufsize_in, + "(undocumented)", NULL, 0}, + {NULL, 0, NULL, NULL, NULL, 0} +}; + +static struct audio_pcm_ops dsound_pcm_ops = { + dsound_init_out, + dsound_fini_out, + dsound_run_out, + dsound_write, + dsound_ctl_out, + + dsound_init_in, + dsound_fini_in, + dsound_run_in, + dsound_read, + dsound_ctl_in +}; + +struct audio_driver dsound_audio_driver = { + INIT_FIELD (name = ) "dsound", + INIT_FIELD (descr = ) + "DirectSound http://wikipedia.org/wiki/DirectSound", + INIT_FIELD (options = ) dsound_options, + INIT_FIELD (init = ) dsound_audio_init, + INIT_FIELD (fini = ) dsound_audio_fini, + INIT_FIELD (pcm_ops = ) &dsound_pcm_ops, + INIT_FIELD (can_be_default = ) 1, + INIT_FIELD (max_voices_out = ) INT_MAX, + INIT_FIELD (max_voices_in = ) 1, + INIT_FIELD (voice_size_out = ) sizeof (DSoundVoiceOut), + INIT_FIELD (voice_size_in = ) sizeof (DSoundVoiceIn) +}; diff --git a/tools/ioemu/audio/fmodaudio.c b/tools/ioemu/audio/fmodaudio.c new file mode 100644 index 0000000000..072d8a830a --- /dev/null +++ b/tools/ioemu/audio/fmodaudio.c @@ -0,0 +1,683 @@ +/* + * QEMU FMOD audio driver + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include "vl.h" + +#define AUDIO_CAP "fmod" +#include "audio_int.h" + +typedef struct FMODVoiceOut { + HWVoiceOut hw; + unsigned int old_pos; + FSOUND_SAMPLE *fmod_sample; + int channel; +} FMODVoiceOut; + +typedef struct FMODVoiceIn { + HWVoiceIn hw; + FSOUND_SAMPLE *fmod_sample; +} FMODVoiceIn; + +static struct { + const char *drvname; + int nb_samples; + int freq; + int nb_channels; + int bufsize; + int threshold; + int broken_adc; +} conf = { + NULL, + 2048 * 2, + 44100, + 2, + 0, + 0, + 0 +}; + +static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", + FMOD_ErrorString (FSOUND_GetError ())); +} + +static void GCC_FMT_ATTR (2, 3) fmod_logerr2 ( + const char *typ, + const char *fmt, + ... + ) +{ + va_list ap; + + AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", + FMOD_ErrorString (FSOUND_GetError ())); +} + +static int fmod_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static void fmod_clear_sample (FMODVoiceOut *fmd) +{ + HWVoiceOut *hw = &fmd->hw; + int status; + void *p1 = 0, *p2 = 0; + unsigned int len1 = 0, len2 = 0; + + status = FSOUND_Sample_Lock ( + fmd->fmod_sample, + 0, + hw->samples << hw->info.shift, + &p1, + &p2, + &len1, + &len2 + ); + + if (!status) { + fmod_logerr ("Failed to lock sample\n"); + return; + } + + if ((len1 & hw->info.align) || (len2 & hw->info.align)) { + dolog ("Lock returned misaligned length %d, %d, alignment %d\n", + len1, len2, hw->info.align + 1); + goto fail; + } + + if ((len1 + len2) - (hw->samples << hw->info.shift)) { + dolog ("Lock returned incomplete length %d, %d\n", + len1 + len2, hw->samples << hw->info.shift); + goto fail; + } + + audio_pcm_info_clear_buf (&hw->info, p1, hw->samples); + + fail: + status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2); + if (!status) { + fmod_logerr ("Failed to unlock sample\n"); + } +} + +static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) +{ + int src_len1 = dst_len; + int src_len2 = 0; + int pos = hw->rpos + dst_len; + st_sample_t *src1 = hw->mix_buf + hw->rpos; + st_sample_t *src2 = NULL; + + if (pos > hw->samples) { + src_len1 = hw->samples - hw->rpos; + src2 = hw->mix_buf; + src_len2 = dst_len - src_len1; + pos = src_len2; + } + + if (src_len1) { + hw->clip (dst, src1, src_len1); + mixeng_clear (src1, src_len1); + } + + if (src_len2) { + dst = advance (dst, src_len1 << hw->info.shift); + hw->clip (dst, src2, src_len2); + mixeng_clear (src2, src_len2); + } + + hw->rpos = pos % hw->samples; +} + +static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2, + unsigned int blen1, unsigned int blen2) +{ + int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2); + if (!status) { + fmod_logerr ("Failed to unlock sample\n"); + return -1; + } + return 0; +} + +static int fmod_lock_sample ( + FSOUND_SAMPLE *sample, + struct audio_pcm_info *info, + int pos, + int len, + void **p1, + void **p2, + unsigned int *blen1, + unsigned int *blen2 + ) +{ + int status; + + status = FSOUND_Sample_Lock ( + sample, + pos << info->shift, + len << info->shift, + p1, + p2, + blen1, + blen2 + ); + + if (!status) { + fmod_logerr ("Failed to lock sample\n"); + return -1; + } + + if ((*blen1 & info->align) || (*blen2 & info->align)) { + dolog ("Lock returned misaligned length %d, %d, alignment %d\n", + *blen1, *blen2, info->align + 1); + + fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2); + + *p1 = NULL - 1; + *p2 = NULL - 1; + *blen1 = ~0U; + *blen2 = ~0U; + return -1; + } + + if (!*p1 && *blen1) { + dolog ("warning: !p1 && blen1=%d\n", *blen1); + *blen1 = 0; + } + + if (!p2 && *blen2) { + dolog ("warning: !p2 && blen2=%d\n", *blen2); + *blen2 = 0; + } + + return 0; +} + +static int fmod_run_out (HWVoiceOut *hw) +{ + FMODVoiceOut *fmd = (FMODVoiceOut *) hw; + int live, decr; + void *p1 = 0, *p2 = 0; + unsigned int blen1 = 0, blen2 = 0; + unsigned int len1 = 0, len2 = 0; + int nb_live; + + live = audio_pcm_hw_get_live_out2 (hw, &nb_live); + if (!live) { + return 0; + } + + if (!hw->pending_disable + && nb_live + && (conf.threshold && live <= conf.threshold)) { + ldebug ("live=%d nb_live=%d\n", live, nb_live); + return 0; + } + + decr = live; + + if (fmd->channel >= 0) { + int len = decr; + int old_pos = fmd->old_pos; + int ppos = FSOUND_GetCurrentPosition (fmd->channel); + + if (ppos == old_pos || !ppos) { + return 0; + } + + if ((old_pos < ppos) && ((old_pos + len) > ppos)) { + len = ppos - old_pos; + } + else { + if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) { + len = hw->samples - old_pos + ppos; + } + } + decr = len; + + if (audio_bug (AUDIO_FUNC, decr < 0)) { + dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n", + decr, live, ppos, old_pos, len); + return 0; + } + } + + + if (!decr) { + return 0; + } + + if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info, + fmd->old_pos, decr, + &p1, &p2, + &blen1, &blen2)) { + return 0; + } + + len1 = blen1 >> hw->info.shift; + len2 = blen2 >> hw->info.shift; + ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2); + decr = len1 + len2; + + if (p1 && len1) { + fmod_write_sample (hw, p1, len1); + } + + if (p2 && len2) { + fmod_write_sample (hw, p2, len2); + } + + fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); + + fmd->old_pos = (fmd->old_pos + decr) % hw->samples; + return decr; +} + +static int aud_to_fmodfmt (audfmt_e fmt, int stereo) +{ + int mode = FSOUND_LOOP_NORMAL; + + switch (fmt) { + case AUD_FMT_S8: + mode |= FSOUND_SIGNED | FSOUND_8BITS; + break; + + case AUD_FMT_U8: + mode |= FSOUND_UNSIGNED | FSOUND_8BITS; + break; + + case AUD_FMT_S16: + mode |= FSOUND_SIGNED | FSOUND_16BITS; + break; + + case AUD_FMT_U16: + mode |= FSOUND_UNSIGNED | FSOUND_16BITS; + break; + + default: + dolog ("Internal logic error: Bad audio format %d\n", fmt); +#ifdef DEBUG_FMOD + abort (); +#endif + mode |= FSOUND_8BITS; + } + mode |= stereo ? FSOUND_STEREO : FSOUND_MONO; + return mode; +} + +static void fmod_fini_out (HWVoiceOut *hw) +{ + FMODVoiceOut *fmd = (FMODVoiceOut *) hw; + + if (fmd->fmod_sample) { + FSOUND_Sample_Free (fmd->fmod_sample); + fmd->fmod_sample = 0; + + if (fmd->channel >= 0) { + FSOUND_StopSound (fmd->channel); + } + } +} + +static int fmod_init_out (HWVoiceOut *hw, audsettings_t *as) +{ + int bits16, mode, channel; + FMODVoiceOut *fmd = (FMODVoiceOut *) hw; + + mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); + fmd->fmod_sample = FSOUND_Sample_Alloc ( + FSOUND_FREE, /* index */ + conf.nb_samples, /* length */ + mode, /* mode */ + as->freq, /* freq */ + 255, /* volume */ + 128, /* pan */ + 255 /* priority */ + ); + + if (!fmd->fmod_sample) { + fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n"); + return -1; + } + + channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1); + if (channel < 0) { + fmod_logerr2 ("DAC", "Failed to start playing sound\n"); + FSOUND_Sample_Free (fmd->fmod_sample); + return -1; + } + fmd->channel = channel; + + /* FMOD always operates on little endian frames? */ + audio_pcm_init_info (&hw->info, as, audio_need_to_swap_endian (0)); + bits16 = (mode & FSOUND_16BITS) != 0; + hw->samples = conf.nb_samples; + return 0; +} + +static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + int status; + FMODVoiceOut *fmd = (FMODVoiceOut *) hw; + + switch (cmd) { + case VOICE_ENABLE: + fmod_clear_sample (fmd); + status = FSOUND_SetPaused (fmd->channel, 0); + if (!status) { + fmod_logerr ("Failed to resume channel %d\n", fmd->channel); + } + break; + + case VOICE_DISABLE: + status = FSOUND_SetPaused (fmd->channel, 1); + if (!status) { + fmod_logerr ("Failed to pause channel %d\n", fmd->channel); + } + break; + } + return 0; +} + +static int fmod_init_in (HWVoiceIn *hw, audsettings_t *as) +{ + int bits16, mode; + FMODVoiceIn *fmd = (FMODVoiceIn *) hw; + + if (conf.broken_adc) { + return -1; + } + + mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); + fmd->fmod_sample = FSOUND_Sample_Alloc ( + FSOUND_FREE, /* index */ + conf.nb_samples, /* length */ + mode, /* mode */ + as->freq, /* freq */ + 255, /* volume */ + 128, /* pan */ + 255 /* priority */ + ); + + if (!fmd->fmod_sample) { + fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n"); + return -1; + } + + /* FMOD always operates on little endian frames? */ + audio_pcm_init_info (&hw->info, as, audio_need_to_swap_endian (0)); + bits16 = (mode & FSOUND_16BITS) != 0; + hw->samples = conf.nb_samples; + return 0; +} + +static void fmod_fini_in (HWVoiceIn *hw) +{ + FMODVoiceIn *fmd = (FMODVoiceIn *) hw; + + if (fmd->fmod_sample) { + FSOUND_Record_Stop (); + FSOUND_Sample_Free (fmd->fmod_sample); + fmd->fmod_sample = 0; + } +} + +static int fmod_run_in (HWVoiceIn *hw) +{ + FMODVoiceIn *fmd = (FMODVoiceIn *) hw; + int hwshift = hw->info.shift; + int live, dead, new_pos, len; + unsigned int blen1 = 0, blen2 = 0; + unsigned int len1, len2; + unsigned int decr; + void *p1, *p2; + + live = audio_pcm_hw_get_live_in (hw); + dead = hw->samples - live; + if (!dead) { + return 0; + } + + new_pos = FSOUND_Record_GetPosition (); + if (new_pos < 0) { + fmod_logerr ("Could not get recording position\n"); + return 0; + } + + len = audio_ring_dist (new_pos, hw->wpos, hw->samples); + if (!len) { + return 0; + } + len = audio_MIN (len, dead); + + if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info, + hw->wpos, len, + &p1, &p2, + &blen1, &blen2)) { + return 0; + } + + len1 = blen1 >> hwshift; + len2 = blen2 >> hwshift; + decr = len1 + len2; + + if (p1 && blen1) { + hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); + } + if (p2 && len2) { + hw->conv (hw->conv_buf, p2, len2, &nominal_volume); + } + + fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); + hw->wpos = (hw->wpos + decr) % hw->samples; + return decr; +} + +static struct { + const char *name; + int type; +} drvtab[] = { + {"none", FSOUND_OUTPUT_NOSOUND}, +#ifdef _WIN32 + {"winmm", FSOUND_OUTPUT_WINMM}, + {"dsound", FSOUND_OUTPUT_DSOUND}, + {"a3d", FSOUND_OUTPUT_A3D}, + {"asio", FSOUND_OUTPUT_ASIO}, +#endif +#ifdef __linux__ + {"oss", FSOUND_OUTPUT_OSS}, + {"alsa", FSOUND_OUTPUT_ALSA}, + {"esd", FSOUND_OUTPUT_ESD}, +#endif +#ifdef __APPLE__ + {"mac", FSOUND_OUTPUT_MAC}, +#endif +#if 0 + {"xbox", FSOUND_OUTPUT_XBOX}, + {"ps2", FSOUND_OUTPUT_PS2}, + {"gcube", FSOUND_OUTPUT_GC}, +#endif + {"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME} +}; + +static void *fmod_audio_init (void) +{ + size_t i; + double ver; + int status; + int output_type = -1; + const char *drv = conf.drvname; + + ver = FSOUND_GetVersion (); + if (ver < FMOD_VERSION) { + dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION); + return NULL; + } + +#ifdef __linux__ + if (ver < 3.75) { + dolog ("FMOD before 3.75 has bug preventing ADC from working\n" + "ADC will be disabled.\n"); + conf.broken_adc = 1; + } +#endif + + if (drv) { + int found = 0; + for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { + if (!strcmp (drv, drvtab[i].name)) { + output_type = drvtab[i].type; + found = 1; + break; + } + } + if (!found) { + dolog ("Unknown FMOD driver `%s'\n", drv); + dolog ("Valid drivers:\n"); + for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { + dolog (" %s\n", drvtab[i].name); + } + } + } + + if (output_type != -1) { + status = FSOUND_SetOutput (output_type); + if (!status) { + fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type); + return NULL; + } + } + + if (conf.bufsize) { + status = FSOUND_SetBufferSize (conf.bufsize); + if (!status) { + fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize); + } + } + + status = FSOUND_Init (conf.freq, conf.nb_channels, 0); + if (!status) { + fmod_logerr ("FSOUND_Init failed\n"); + return NULL; + } + + return &conf; +} + +static int fmod_read (SWVoiceIn *sw, void *buf, int size) +{ + return audio_pcm_sw_read (sw, buf, size); +} + +static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...) +{ + int status; + FMODVoiceIn *fmd = (FMODVoiceIn *) hw; + + switch (cmd) { + case VOICE_ENABLE: + status = FSOUND_Record_StartSample (fmd->fmod_sample, 1); + if (!status) { + fmod_logerr ("Failed to start recording\n"); + } + break; + + case VOICE_DISABLE: + status = FSOUND_Record_Stop (); + if (!status) { + fmod_logerr ("Failed to stop recording\n"); + } + break; + } + return 0; +} + +static void fmod_audio_fini (void *opaque) +{ + (void) opaque; + FSOUND_Close (); +} + +static struct audio_option fmod_options[] = { + {"DRV", AUD_OPT_STR, &conf.drvname, + "FMOD driver", NULL, 0}, + {"FREQ", AUD_OPT_INT, &conf.freq, + "Default frequency", NULL, 0}, + {"SAMPLES", AUD_OPT_INT, &conf.nb_samples, + "Buffer size in samples", NULL, 0}, + {"CHANNELS", AUD_OPT_INT, &conf.nb_channels, + "Number of default channels (1 - mono, 2 - stereo)", NULL, 0}, + {"BUFSIZE", AUD_OPT_INT, &conf.bufsize, + "(undocumented)", NULL, 0}, +#if 0 + {"THRESHOLD", AUD_OPT_INT, &conf.threshold, + "(undocumented)"}, +#endif + + {NULL, 0, NULL, NULL, NULL, 0} +}; + +static struct audio_pcm_ops fmod_pcm_ops = { + fmod_init_out, + fmod_fini_out, + fmod_run_out, + fmod_write, + fmod_ctl_out, + + fmod_init_in, + fmod_fini_in, + fmod_run_in, + fmod_read, + fmod_ctl_in +}; + +struct audio_driver fmod_audio_driver = { + INIT_FIELD (name = ) "fmod", + INIT_FIELD (descr = ) "FMOD 3.xx http://www.fmod.org", + INIT_FIELD (options = ) fmod_options, + INIT_FIELD (init = ) fmod_audio_init, + INIT_FIELD (fini = ) fmod_audio_fini, + INIT_FIELD (pcm_ops = ) &fmod_pcm_ops, + INIT_FIELD (can_be_default = ) 1, + INIT_FIELD (max_voices_out = ) INT_MAX, + INIT_FIELD (max_voices_in = ) INT_MAX, + INIT_FIELD (voice_size_out = ) sizeof (FMODVoiceOut), + INIT_FIELD (voice_size_in = ) sizeof (FMODVoiceIn) +}; diff --git a/tools/ioemu/audio/mixeng.c b/tools/ioemu/audio/mixeng.c new file mode 100644 index 0000000000..6308d41004 --- /dev/null +++ b/tools/ioemu/audio/mixeng.c @@ -0,0 +1,277 @@ +/* + * QEMU Mixing engine + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * Copyright (c) 1998 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#define AUDIO_CAP "mixeng" +#include "audio_int.h" + +#define NOVOL + +/* 8 bit */ +#define ENDIAN_CONVERSION natural +#define ENDIAN_CONVERT(v) (v) + +/* Signed 8 bit */ +#define IN_T int8_t +#define IN_MIN SCHAR_MIN +#define IN_MAX SCHAR_MAX +#define SIGNED +#define SHIFT 8 +#include "mixeng_template.h" +#undef SIGNED +#undef IN_MAX +#undef IN_MIN +#undef IN_T +#undef SHIFT + +/* Unsigned 8 bit */ +#define IN_T uint8_t +#define IN_MIN 0 +#define IN_MAX UCHAR_MAX +#define SHIFT 8 +#include "mixeng_template.h" +#undef IN_MAX +#undef IN_MIN +#undef IN_T +#undef SHIFT + +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION + +/* Signed 16 bit */ +#define IN_T int16_t +#define IN_MIN SHRT_MIN +#define IN_MAX SHRT_MAX +#define SIGNED +#define SHIFT 16 +#define ENDIAN_CONVERSION natural +#define ENDIAN_CONVERT(v) (v) +#include "mixeng_template.h" +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION +#define ENDIAN_CONVERSION swap +#define ENDIAN_CONVERT(v) bswap16 (v) +#include "mixeng_template.h" +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION +#undef SIGNED +#undef IN_MAX +#undef IN_MIN +#undef IN_T +#undef SHIFT + +#define IN_T uint16_t +#define IN_MIN 0 +#define IN_MAX USHRT_MAX +#define SHIFT 16 +#define ENDIAN_CONVERSION natural +#define ENDIAN_CONVERT(v) (v) +#include "mixeng_template.h" +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION +#define ENDIAN_CONVERSION swap +#define ENDIAN_CONVERT(v) bswap16 (v) +#include "mixeng_template.h" +#undef ENDIAN_CONVERT +#undef ENDIAN_CONVERSION +#undef IN_MAX +#undef IN_MIN +#undef IN_T +#undef SHIFT + +t_sample *mixeng_conv[2][2][2][2] = { + { + { + { + conv_natural_uint8_t_to_mono, + conv_natural_uint16_t_to_mono + }, + { + conv_natural_uint8_t_to_mono, + conv_swap_uint16_t_to_mono + } + }, + { + { + conv_natural_int8_t_to_mono, + conv_natural_int16_t_to_mono + }, + { + conv_natural_int8_t_to_mono, + conv_swap_int16_t_to_mono + } + } + }, + { + { + { + conv_natural_uint8_t_to_stereo, + conv_natural_uint16_t_to_stereo + }, + { + conv_natural_uint8_t_to_stereo, + conv_swap_uint16_t_to_stereo + } + }, + { + { + conv_natural_int8_t_to_stereo, + conv_natural_int16_t_to_stereo + }, + { + conv_natural_int8_t_to_stereo, + conv_swap_int16_t_to_stereo + } + } + } +}; + +f_sample *mixeng_clip[2][2][2][2] = { + { + { + { + clip_natural_uint8_t_from_mono, + clip_natural_uint16_t_from_mono + }, + { + clip_natural_uint8_t_from_mono, + clip_swap_uint16_t_from_mono + } + }, + { + { + clip_natural_int8_t_from_mono, + clip_natural_int16_t_from_mono + }, + { + clip_natural_int8_t_from_mono, + clip_swap_int16_t_from_mono + } + } + }, + { + { + { + clip_natural_uint8_t_from_stereo, + clip_natural_uint16_t_from_stereo + }, + { + clip_natural_uint8_t_from_stereo, + clip_swap_uint16_t_from_stereo + } + }, + { + { + clip_natural_int8_t_from_stereo, + clip_natural_int16_t_from_stereo + }, + { + clip_natural_int8_t_from_stereo, + clip_swap_int16_t_from_stereo + } + } + } +}; + +/* + * August 21, 1998 + * Copyright 1998 Fabrice Bellard. + * + * [Rewrote completly the code of Lance Norskog And Sundry + * Contributors with a more efficient algorithm.] + * + * This source code is freely redistributable and may be used for + * any purpose. This copyright notice must be maintained. + * Lance Norskog And Sundry Contributors are not responsible for + * the consequences of using this software. + */ + +/* + * Sound Tools rate change effect file. + */ +/* + * Linear Interpolation. + * + * The use of fractional increment allows us to use no buffer. It + * avoid the problems at the end of the buffer we had with the old + * method which stored a possibly big buffer of size + * lcm(in_rate,out_rate). + * + * Limited to 16 bit samples and sampling frequency <= 65535 Hz. If + * the input & output frequencies are equal, a delay of one sample is + * introduced. Limited to processing 32-bit count worth of samples. + * + * 1 << FRAC_BITS evaluating to zero in several places. Changed with + * an (unsigned long) cast to make it safe. MarkMLl 2/1/99 + */ + +/* Private data */ +struct rate { + uint64_t opos; + uint64_t opos_inc; + uint32_t ipos; /* position in the input stream (integer) */ + st_sample_t ilast; /* last sample in the input stream */ +}; + +/* + * Prepare processing. + */ +void *st_rate_start (int inrate, int outrate) +{ + struct rate *rate = audio_calloc (AUDIO_FUNC, 1, sizeof (*rate)); + + if (!rate) { + dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate)); + return NULL; + } + + rate->opos = 0; + + /* increment */ + rate->opos_inc = ((uint64_t) inrate << 32) / outrate; + + rate->ipos = 0; + rate->ilast.l = 0; + rate->ilast.r = 0; + return rate; +} + +#define NAME st_rate_flow_mix +#define OP(a, b) a += b +#include "rate_template.h" + +#define NAME st_rate_flow +#define OP(a, b) a = b +#include "rate_template.h" + +void st_rate_stop (void *opaque) +{ + qemu_free (opaque); +} + +void mixeng_clear (st_sample_t *buf, int len) +{ + memset (buf, 0, len * sizeof (st_sample_t)); +} diff --git a/tools/ioemu/audio/mixeng.h b/tools/ioemu/audio/mixeng.h new file mode 100644 index 0000000000..9e3bac1744 --- /dev/null +++ b/tools/ioemu/audio/mixeng.h @@ -0,0 +1,51 @@ +/* + * QEMU Mixing engine header + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_MIXENG_H +#define QEMU_MIXENG_H + +#ifdef FLOAT_MIXENG +typedef float real_t; +typedef struct { int mute; real_t r; real_t l; } volume_t; +typedef struct { real_t l; real_t r; } st_sample_t; +#else +typedef struct { int mute; int64_t r; int64_t l; } volume_t; +typedef struct { int64_t l; int64_t r; } st_sample_t; +#endif + +typedef void (t_sample) (st_sample_t *dst, const void *src, + int samples, volume_t *vol); +typedef void (f_sample) (void *dst, const st_sample_t *src, int samples); + +extern t_sample *mixeng_conv[2][2][2][2]; +extern f_sample *mixeng_clip[2][2][2][2]; + +void *st_rate_start (int inrate, int outrate); +void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf, + int *isamp, int *osamp); +void st_rate_flow_mix (void *opaque, st_sample_t *ibuf, st_sample_t *obuf, + int *isamp, int *osamp); +void st_rate_stop (void *opaque); +void mixeng_clear (st_sample_t *buf, int len); + +#endif /* mixeng.h */ diff --git a/tools/ioemu/audio/mixeng_template.h b/tools/ioemu/audio/mixeng_template.h new file mode 100644 index 0000000000..d726441e2e --- /dev/null +++ b/tools/ioemu/audio/mixeng_template.h @@ -0,0 +1,177 @@ +/* + * QEMU Mixing engine + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Tusen tack till Mike Nordell + * dec++'ified by Dscho + */ + +#ifndef SIGNED +#define HALF (IN_MAX >> 1) +#endif + +#ifdef NOVOL +#define VOL(a, b) a +#else +#ifdef FLOAT_MIXENG +#define VOL(a, b) ((a) * (b)) +#else +#define VOL(a, b) ((a) * (b)) >> 32 +#endif +#endif + +#define ET glue (ENDIAN_CONVERSION, glue (_, IN_T)) + +#ifdef FLOAT_MIXENG +static real_t inline glue (conv_, ET) (IN_T v) +{ + IN_T nv = ENDIAN_CONVERT (v); + +#ifdef RECIPROCAL +#ifdef SIGNED + return nv * (1.f / (real_t) (IN_MAX - IN_MIN)); +#else + return (nv - HALF) * (1.f / (real_t) IN_MAX); +#endif +#else /* !RECIPROCAL */ +#ifdef SIGNED + return nv / (real_t) (IN_MAX - IN_MIN); +#else + return (nv - HALF) / (real_t) IN_MAX; +#endif +#endif +} + +static IN_T inline glue (clip_, ET) (real_t v) +{ + if (v >= 0.5) { + return IN_MAX; + } + else if (v < -0.5) { + return IN_MIN; + } + +#ifdef SIGNED + return ENDIAN_CONVERT ((IN_T) (v * (IN_MAX - IN_MIN))); +#else + return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF)); +#endif +} + +#else /* !FLOAT_MIXENG */ + +static inline int64_t glue (conv_, ET) (IN_T v) +{ + IN_T nv = ENDIAN_CONVERT (v); +#ifdef SIGNED + return ((int64_t) nv) << (32 - SHIFT); +#else + return ((int64_t) nv - HALF) << (32 - SHIFT); +#endif +} + +static inline IN_T glue (clip_, ET) (int64_t v) +{ + if (v >= 0x7f000000) { + return IN_MAX; + } + else if (v < -2147483648LL) { + return IN_MIN; + } + +#ifdef SIGNED + return ENDIAN_CONVERT ((IN_T) (v >> (32 - SHIFT))); +#else + return ENDIAN_CONVERT ((IN_T) ((v >> (32 - SHIFT)) + HALF)); +#endif +} +#endif + +static void glue (glue (conv_, ET), _to_stereo) + (st_sample_t *dst, const void *src, int samples, volume_t *vol) +{ + st_sample_t *out = dst; + IN_T *in = (IN_T *) src; +#ifndef NOVOL + if (vol->mute) { + mixeng_clear (dst, samples); + return; + } +#else + (void) vol; +#endif + while (samples--) { + out->l = VOL (glue (conv_, ET) (*in++), vol->l); + out->r = VOL (glue (conv_, ET) (*in++), vol->r); + out += 1; + } +} + +static void glue (glue (conv_, ET), _to_mono) + (st_sample_t *dst, const void *src, int samples, volume_t *vol) +{ + st_sample_t *out = dst; + IN_T *in = (IN_T *) src; +#ifndef NOVOL + if (vol->mute) { + mixeng_clear (dst, samples); + return; + } +#else + (void) vol; +#endif + while (samples--) { + out->l = VOL (glue (conv_, ET) (in[0]), vol->l); + out->r = out->l; + out += 1; + in += 1; + } +} + +static void glue (glue (clip_, ET), _from_stereo) + (void *dst, const st_sample_t *src, int samples) +{ + const st_sample_t *in = src; + IN_T *out = (IN_T *) dst; + while (samples--) { + *out++ = glue (clip_, ET) (in->l); + *out++ = glue (clip_, ET) (in->r); + in += 1; + } +} + +static void glue (glue (clip_, ET), _from_mono) + (void *dst, const st_sample_t *src, int samples) +{ + const st_sample_t *in = src; + IN_T *out = (IN_T *) dst; + while (samples--) { + *out++ = glue (clip_, ET) (in->l + in->r); + in += 1; + } +} + +#undef ET +#undef HALF +#undef VOL diff --git a/tools/ioemu/audio/noaudio.c b/tools/ioemu/audio/noaudio.c new file mode 100644 index 0000000000..aa3581168d --- /dev/null +++ b/tools/ioemu/audio/noaudio.c @@ -0,0 +1,170 @@ +/* + * QEMU Timer based audio emulation + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#define AUDIO_CAP "noaudio" +#include "audio_int.h" + +typedef struct NoVoiceOut { + HWVoiceOut hw; + int64_t old_ticks; +} NoVoiceOut; + +typedef struct NoVoiceIn { + HWVoiceIn hw; + int64_t old_ticks; +} NoVoiceIn; + +static int no_run_out (HWVoiceOut *hw) +{ + NoVoiceOut *no = (NoVoiceOut *) hw; + int live, decr, samples; + int64_t now = qemu_get_clock (vm_clock); + int64_t ticks = now - no->old_ticks; + int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec; + + if (bytes > INT_MAX) { + samples = INT_MAX >> hw->info.shift; + } + else { + samples = bytes >> hw->info.shift; + } + + live = audio_pcm_hw_get_live_out (&no->hw); + if (!live) { + return 0; + } + + no->old_ticks = now; + decr = audio_MIN (live, samples); + hw->rpos = (hw->rpos + decr) % hw->samples; + return decr; +} + +static int no_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int no_init_out (HWVoiceOut *hw, audsettings_t *as) +{ + audio_pcm_init_info (&hw->info, as, 0); + hw->samples = 1024; + return 0; +} + +static void no_fini_out (HWVoiceOut *hw) +{ + (void) hw; +} + +static int no_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + (void) hw; + (void) cmd; + return 0; +} + +static int no_init_in (HWVoiceIn *hw, audsettings_t *as) +{ + audio_pcm_init_info (&hw->info, as, 0); + hw->samples = 1024; + return 0; +} + +static void no_fini_in (HWVoiceIn *hw) +{ + (void) hw; +} + +static int no_run_in (HWVoiceIn *hw) +{ + NoVoiceIn *no = (NoVoiceIn *) hw; + int64_t now = qemu_get_clock (vm_clock); + int64_t ticks = now - no->old_ticks; + int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec; + int live = audio_pcm_hw_get_live_in (hw); + int dead = hw->samples - live; + int samples; + + bytes = audio_MIN (bytes, INT_MAX); + samples = bytes >> hw->info.shift; + samples = audio_MIN (samples, dead); + + return samples; +} + +static int no_read (SWVoiceIn *sw, void *buf, int size) +{ + int samples = size >> sw->info.shift; + int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; + int to_clear = audio_MIN (samples, total); + audio_pcm_info_clear_buf (&sw->info, buf, to_clear); + return to_clear; +} + +static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) +{ + (void) hw; + (void) cmd; + return 0; +} + +static void *no_audio_init (void) +{ + return &no_audio_init; +} + +static void no_audio_fini (void *opaque) +{ + (void) opaque; +} + +static struct audio_pcm_ops no_pcm_ops = { + no_init_out, + no_fini_out, + no_run_out, + no_write, + no_ctl_out, + + no_init_in, + no_fini_in, + no_run_in, + no_read, + no_ctl_in +}; + +struct audio_driver no_audio_driver = { + INIT_FIELD (name = ) "none", + INIT_FIELD (descr = ) "Timer based audio emulation", + INIT_FIELD (options = ) NULL, + INIT_FIELD (init = ) no_audio_init, + INIT_FIELD (fini = ) no_audio_fini, + INIT_FIELD (pcm_ops = ) &no_pcm_ops, + INIT_FIELD (can_be_default = ) 1, + INIT_FIELD (max_voices_out = ) INT_MAX, + INIT_FIELD (max_voices_in = ) INT_MAX, + INIT_FIELD (voice_size_out = ) sizeof (NoVoiceOut), + INIT_FIELD (voice_size_in = ) sizeof (NoVoiceIn) +}; diff --git a/tools/ioemu/audio/ossaudio.c b/tools/ioemu/audio/ossaudio.c new file mode 100644 index 0000000000..7d12f9e34a --- /dev/null +++ b/tools/ioemu/audio/ossaudio.c @@ -0,0 +1,762 @@ +/* + * QEMU OSS audio driver + * + * Copyright (c) 2003-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include "vl.h" + +#define AUDIO_CAP "oss" +#include "audio_int.h" + +typedef struct OSSVoiceOut { + HWVoiceOut hw; + void *pcm_buf; + int fd; + int nfrags; + int fragsize; + int mmapped; + int old_optr; +} OSSVoiceOut; + +typedef struct OSSVoiceIn { + HWVoiceIn hw; + void *pcm_buf; + int fd; + int nfrags; + int fragsize; + int old_optr; +} OSSVoiceIn; + +static struct { + int try_mmap; + int nfrags; + int fragsize; + const char *devpath_out; + const char *devpath_in; +} conf = { + .try_mmap = 0, + .nfrags = 4, + .fragsize = 4096, + .devpath_out = "/dev/dsp", + .devpath_in = "/dev/dsp" +}; + +struct oss_params { + int freq; + audfmt_e fmt; + int nchannels; + int nfrags; + int fragsize; +}; + +static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); +} + +static void GCC_FMT_ATTR (3, 4) oss_logerr2 ( + int err, + const char *typ, + const char *fmt, + ... + ) +{ + va_list ap; + + AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); +} + +static void oss_anal_close (int *fdp) +{ + int err = close (*fdp); + if (err) { + oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp); + } + *fdp = -1; +} + +static int oss_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int aud_to_ossfmt (audfmt_e fmt) +{ + switch (fmt) { + case AUD_FMT_S8: + return AFMT_S8; + + case AUD_FMT_U8: + return AFMT_U8; + + case AUD_FMT_S16: + return AFMT_S16_LE; + + case AUD_FMT_U16: + return AFMT_U16_LE; + + default: + dolog ("Internal logic error: Bad audio format %d\n", fmt); +#ifdef DEBUG_AUDIO + abort (); +#endif + return AFMT_U8; + } +} + +static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness) +{ + switch (ossfmt) { + case AFMT_S8: + *endianness =0; + *fmt = AUD_FMT_S8; + break; + + case AFMT_U8: + *endianness = 0; + *fmt = AUD_FMT_U8; + break; + + case AFMT_S16_LE: + *endianness = 0; + *fmt = AUD_FMT_S16; + break; + + case AFMT_U16_LE: + *endianness = 0; + *fmt = AUD_FMT_U16; + break; + + case AFMT_S16_BE: + *endianness = 1; + *fmt = AUD_FMT_S16; + break; + + case AFMT_U16_BE: + *endianness = 1; + *fmt = AUD_FMT_U16; + break; + + default: + dolog ("Unrecognized audio format %d\n", ossfmt); + return -1; + } + + return 0; +} + +#if defined DEBUG_MISMATCHES || defined DEBUG +static void oss_dump_info (struct oss_params *req, struct oss_params *obt) +{ + dolog ("parameter | requested value | obtained value\n"); + dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); + dolog ("channels | %10d | %10d\n", + req->nchannels, obt->nchannels); + dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); + dolog ("nfrags | %10d | %10d\n", req->nfrags, obt->nfrags); + dolog ("fragsize | %10d | %10d\n", + req->fragsize, obt->fragsize); +} +#endif + +static int oss_open (int in, struct oss_params *req, + struct oss_params *obt, int *pfd) +{ + int fd; + int mmmmssss; + audio_buf_info abinfo; + int fmt, freq, nchannels; + const char *dspname = in ? conf.devpath_in : conf.devpath_out; + const char *typ = in ? "ADC" : "DAC"; + + fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK); + if (-1 == fd) { + oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname); + return -1; + } + + freq = req->freq; + nchannels = req->nchannels; + fmt = req->fmt; + + if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { + oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt); + goto err; + } + + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) { + oss_logerr2 (errno, typ, "Failed to set number of channels %d\n", + req->nchannels); + goto err; + } + + if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) { + oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq); + goto err; + } + + if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) { + oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n"); + goto err; + } + + mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize); + if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) { + oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n", + req->nfrags, req->fragsize); + goto err; + } + + if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) { + oss_logerr2 (errno, typ, "Failed to get buffer length\n"); + goto err; + } + + obt->fmt = fmt; + obt->nchannels = nchannels; + obt->freq = freq; + obt->nfrags = abinfo.fragstotal; + obt->fragsize = abinfo.fragsize; + *pfd = fd; + +#ifdef DEBUG_MISMATCHES + if ((req->fmt != obt->fmt) || + (req->nchannels != obt->nchannels) || + (req->freq != obt->freq) || + (req->fragsize != obt->fragsize) || + (req->nfrags != obt->nfrags)) { + dolog ("Audio parameters mismatch\n"); + oss_dump_info (req, obt); + } +#endif + +#ifdef DEBUG + oss_dump_info (req, obt); +#endif + return 0; + + err: + oss_anal_close (&fd); + return -1; +} + +static int oss_run_out (HWVoiceOut *hw) +{ + OSSVoiceOut *oss = (OSSVoiceOut *) hw; + int err, rpos, live, decr; + int samples; + uint8_t *dst; + st_sample_t *src; + struct audio_buf_info abinfo; + struct count_info cntinfo; + int bufsize; + + live = audio_pcm_hw_get_live_out (hw); + if (!live) { + return 0; + } + + bufsize = hw->samples << hw->info.shift; + + if (oss->mmapped) { + int bytes; + + err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo); + if (err < 0) { + oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n"); + return 0; + } + + if (cntinfo.ptr == oss->old_optr) { + if (abs (hw->samples - live) < 64) { + dolog ("warning: Overrun\n"); + } + return 0; + } + + if (cntinfo.ptr > oss->old_optr) { + bytes = cntinfo.ptr - oss->old_optr; + } + else { + bytes = bufsize + cntinfo.ptr - oss->old_optr; + } + + decr = audio_MIN (bytes >> hw->info.shift, live); + } + else { + err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo); + if (err < 0) { + oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n"); + return 0; + } + + if (abinfo.bytes < 0 || abinfo.bytes > bufsize) { + ldebug ("warning: Invalid available size, size=%d bufsize=%d\n", + abinfo.bytes, bufsize); + return 0; + } + + decr = audio_MIN (abinfo.bytes >> hw->info.shift, live); + if (!decr) { + return 0; + } + } + + samples = decr; + rpos = hw->rpos; + while (samples) { + int left_till_end_samples = hw->samples - rpos; + int convert_samples = audio_MIN (samples, left_till_end_samples); + + src = hw->mix_buf + rpos; + dst = advance (oss->pcm_buf, rpos << hw->info.shift); + + hw->clip (dst, src, convert_samples); + if (!oss->mmapped) { + int written; + + written = write (oss->fd, dst, convert_samples << hw->info.shift); + /* XXX: follow errno recommendations ? */ + if (written == -1) { + oss_logerr ( + errno, + "Failed to write %d bytes of audio data from %p\n", + convert_samples << hw->info.shift, + dst + ); + continue; + } + + if (written != convert_samples << hw->info.shift) { + int wsamples = written >> hw->info.shift; + int wbytes = wsamples << hw->info.shift; + if (wbytes != written) { + dolog ("warning: Misaligned write %d (requested %d), " + "alignment %d\n", + wbytes, written, hw->info.align + 1); + } + mixeng_clear (src, wsamples); + decr -= wsamples; + rpos = (rpos + wsamples) % hw->samples; + break; + } + } + + mixeng_clear (src, convert_samples); + + rpos = (rpos + convert_samples) % hw->samples; + samples -= convert_samples; + } + if (oss->mmapped) { + oss->old_optr = cntinfo.ptr; + } + + hw->rpos = rpos; + return decr; +} + +static void oss_fini_out (HWVoiceOut *hw) +{ + int err; + OSSVoiceOut *oss = (OSSVoiceOut *) hw; + + ldebug ("oss_fini\n"); + oss_anal_close (&oss->fd); + + if (oss->pcm_buf) { + if (oss->mmapped) { + err = munmap (oss->pcm_buf, hw->samples << hw->info.shift); + if (err) { + oss_logerr (errno, "Failed to unmap buffer %p, size %d\n", + oss->pcm_buf, hw->samples << hw->info.shift); + } + } + else { + qemu_free (oss->pcm_buf); + } + oss->pcm_buf = NULL; + } +} + +static int oss_init_out (HWVoiceOut *hw, audsettings_t *as) +{ + OSSVoiceOut *oss = (OSSVoiceOut *) hw; + struct oss_params req, obt; + int endianness; + int err; + int fd; + audfmt_e effective_fmt; + audsettings_t obt_as; + + oss->fd = -1; + + req.fmt = aud_to_ossfmt (as->fmt); + req.freq = as->freq; + req.nchannels = as->nchannels; + req.fragsize = conf.fragsize; + req.nfrags = conf.nfrags; + + if (oss_open (0, &req, &obt, &fd)) { + return -1; + } + + err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness); + if (err) { + oss_anal_close (&fd); + return -1; + } + + obt_as.freq = obt.freq; + obt_as.nchannels = obt.nchannels; + obt_as.fmt = effective_fmt; + + audio_pcm_init_info ( + &hw->info, + &obt_as, + audio_need_to_swap_endian (endianness) + ); + oss->nfrags = obt.nfrags; + oss->fragsize = obt.fragsize; + + if (obt.nfrags * obt.fragsize & hw->info.align) { + dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n", + obt.nfrags * obt.fragsize, hw->info.align + 1); + } + + hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; + + oss->mmapped = 0; + if (conf.try_mmap) { + oss->pcm_buf = mmap ( + 0, + hw->samples << hw->info.shift, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, + 0 + ); + if (oss->pcm_buf == MAP_FAILED) { + oss_logerr (errno, "Failed to map %d bytes of DAC\n", + hw->samples << hw->info.shift); + } else { + int err; + int trig = 0; + if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { + oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); + } + else { + trig = PCM_ENABLE_OUTPUT; + if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { + oss_logerr ( + errno, + "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" + ); + } + else { + oss->mmapped = 1; + } + } + + if (!oss->mmapped) { + err = munmap (oss->pcm_buf, hw->samples << hw->info.shift); + if (err) { + oss_logerr (errno, "Failed to unmap buffer %p size %d\n", + oss->pcm_buf, hw->samples << hw->info.shift); + } + } + } + } + + if (!oss->mmapped) { + oss->pcm_buf = audio_calloc ( + AUDIO_FUNC, + hw->samples, + 1 << hw->info.shift + ); + if (!oss->pcm_buf) { + dolog ( + "Could not allocate DAC buffer (%d samples, each %d bytes)\n", + hw->samples, + 1 << hw->info.shift + ); + oss_anal_close (&fd); + return -1; + } + } + + oss->fd = fd; + return 0; +} + +static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + int trig; + OSSVoiceOut *oss = (OSSVoiceOut *) hw; + + if (!oss->mmapped) { + return 0; + } + + switch (cmd) { + case VOICE_ENABLE: + ldebug ("enabling voice\n"); + audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples); + trig = PCM_ENABLE_OUTPUT; + if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { + oss_logerr ( + errno, + "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" + ); + return -1; + } + break; + + case VOICE_DISABLE: + ldebug ("disabling voice\n"); + trig = 0; + if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { + oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); + return -1; + } + break; + } + return 0; +} + +static int oss_init_in (HWVoiceIn *hw, audsettings_t *as) +{ + OSSVoiceIn *oss = (OSSVoiceIn *) hw; + struct oss_params req, obt; + int endianness; + int err; + int fd; + audfmt_e effective_fmt; + audsettings_t obt_as; + + oss->fd = -1; + + req.fmt = aud_to_ossfmt (as->fmt); + req.freq = as->freq; + req.nchannels = as->nchannels; + req.fragsize = conf.fragsize; + req.nfrags = conf.nfrags; + if (oss_open (1, &req, &obt, &fd)) { + return -1; + } + + err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness); + if (err) { + oss_anal_close (&fd); + return -1; + } + + obt_as.freq = obt.freq; + obt_as.nchannels = obt.nchannels; + obt_as.fmt = effective_fmt; + + audio_pcm_init_info ( + &hw->info, + &obt_as, + audio_need_to_swap_endian (endianness) + ); + oss->nfrags = obt.nfrags; + oss->fragsize = obt.fragsize; + + if (obt.nfrags * obt.fragsize & hw->info.align) { + dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n", + obt.nfrags * obt.fragsize, hw->info.align + 1); + } + + hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; + oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + if (!oss->pcm_buf) { + dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", + hw->samples, 1 << hw->info.shift); + oss_anal_close (&fd); + return -1; + } + + oss->fd = fd; + return 0; +} + +static void oss_fini_in (HWVoiceIn *hw) +{ + OSSVoiceIn *oss = (OSSVoiceIn *) hw; + + oss_anal_close (&oss->fd); + + if (oss->pcm_buf) { + qemu_free (oss->pcm_buf); + oss->pcm_buf = NULL; + } +} + +static int oss_run_in (HWVoiceIn *hw) +{ + OSSVoiceIn *oss = (OSSVoiceIn *) hw; + int hwshift = hw->info.shift; + int i; + int live = audio_pcm_hw_get_live_in (hw); + int dead = hw->samples - live; + size_t read_samples = 0; + struct { + int add; + int len; + } bufs[2] = { + { hw->wpos, 0 }, + { 0, 0 } + }; + + if (!dead) { + return 0; + } + + if (hw->wpos + dead > hw->samples) { + bufs[0].len = (hw->samples - hw->wpos) << hwshift; + bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift; + } + else { + bufs[0].len = dead << hwshift; + } + + + for (i = 0; i < 2; ++i) { + ssize_t nread; + + if (bufs[i].len) { + void *p = advance (oss->pcm_buf, bufs[i].add << hwshift); + nread = read (oss->fd, p, bufs[i].len); + + if (nread > 0) { + if (nread & hw->info.align) { + dolog ("warning: Misaligned read %zd (requested %d), " + "alignment %d\n", nread, bufs[i].add << hwshift, + hw->info.align + 1); + } + read_samples += nread >> hwshift; + hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift, + &nominal_volume); + } + + if (bufs[i].len - nread) { + if (nread == -1) { + switch (errno) { + case EINTR: + case EAGAIN: + break; + default: + oss_logerr ( + errno, + "Failed to read %d bytes of audio (to %p)\n", + bufs[i].len, p + ); + break; + } + } + break; + } + } + } + + hw->wpos = (hw->wpos + read_samples) % hw->samples; + return read_samples; +} + +static int oss_read (SWVoiceIn *sw, void *buf, int size) +{ + return audio_pcm_sw_read (sw, buf, size); +} + +static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) +{ + (void) hw; + (void) cmd; + return 0; +} + +static void *oss_audio_init (void) +{ + return &conf; +} + +static void oss_audio_fini (void *opaque) +{ + (void) opaque; +} + +static struct audio_option oss_options[] = { + {"FRAGSIZE", AUD_OPT_INT, &conf.fragsize, + "Fragment size in bytes", NULL, 0}, + {"NFRAGS", AUD_OPT_INT, &conf.nfrags, + "Number of fragments", NULL, 0}, + {"MMAP", AUD_OPT_BOOL, &conf.try_mmap, + "Try using memory mapped access", NULL, 0}, + {"DAC_DEV", AUD_OPT_STR, &conf.devpath_out, + "Path to DAC device", NULL, 0}, + {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in, + "Path to ADC device", NULL, 0}, + {NULL, 0, NULL, NULL, NULL, 0} +}; + +static struct audio_pcm_ops oss_pcm_ops = { + oss_init_out, + oss_fini_out, + oss_run_out, + oss_write, + oss_ctl_out, + + oss_init_in, + oss_fini_in, + oss_run_in, + oss_read, + oss_ctl_in +}; + +struct audio_driver oss_audio_driver = { + INIT_FIELD (name = ) "oss", + INIT_FIELD (descr = ) "OSS http://www.opensound.com", + INIT_FIELD (options = ) oss_options, + INIT_FIELD (init = ) oss_audio_init, + INIT_FIELD (fini = ) oss_audio_fini, + INIT_FIELD (pcm_ops = ) &oss_pcm_ops, + INIT_FIELD (can_be_default = ) 1, + INIT_FIELD (max_voices_out = ) INT_MAX, + INIT_FIELD (max_voices_in = ) INT_MAX, + INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut), + INIT_FIELD (voice_size_in = ) sizeof (OSSVoiceIn) +}; diff --git a/tools/ioemu/audio/rate_template.h b/tools/ioemu/audio/rate_template.h new file mode 100644 index 0000000000..3e0e77c94e --- /dev/null +++ b/tools/ioemu/audio/rate_template.h @@ -0,0 +1,111 @@ +/* + * QEMU Mixing engine + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * Copyright (c) 1998 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * Processed signed long samples from ibuf to obuf. + * Return number of samples processed. + */ +void NAME (void *opaque, st_sample_t *ibuf, st_sample_t *obuf, + int *isamp, int *osamp) +{ + struct rate *rate = opaque; + st_sample_t *istart, *iend; + st_sample_t *ostart, *oend; + st_sample_t ilast, icur, out; +#ifdef FLOAT_MIXENG + real_t t; +#else + int64_t t; +#endif + + ilast = rate->ilast; + + istart = ibuf; + iend = ibuf + *isamp; + + ostart = obuf; + oend = obuf + *osamp; + + if (rate->opos_inc == (1ULL + UINT_MAX)) { + int i, n = *isamp > *osamp ? *osamp : *isamp; + for (i = 0; i < n; i++) { + OP (obuf[i].l, ibuf[i].r); + OP (obuf[i].r, ibuf[i].r); + } + *isamp = n; + *osamp = n; + return; + } + + while (obuf < oend) { + + /* Safety catch to make sure we have input samples. */ + if (ibuf >= iend) { + break; + } + + /* read as many input samples so that ipos > opos */ + + while (rate->ipos <= (rate->opos >> 32)) { + ilast = *ibuf++; + rate->ipos++; + /* See if we finished the input buffer yet */ + if (ibuf >= iend) { + goto the_end; + } + } + + icur = *ibuf; + + /* interpolate */ +#ifdef FLOAT_MIXENG +#ifdef RECIPROCAL + t = (rate->opos & UINT_MAX) * (1.f / UINT_MAX); +#else + t = (rate->opos & UINT_MAX) / (real_t) UINT_MAX; +#endif + out.l = (ilast.l * (1.0 - t)) + icur.l * t; + out.r = (ilast.r * (1.0 - t)) + icur.r * t; +#else + t = rate->opos & 0xffffffff; + out.l = (ilast.l * ((int64_t) UINT_MAX - t) + icur.l * t) >> 32; + out.r = (ilast.r * ((int64_t) UINT_MAX - t) + icur.r * t) >> 32; +#endif + + /* output sample & increment position */ + OP (obuf->l, out.l); + OP (obuf->r, out.r); + obuf += 1; + rate->opos += rate->opos_inc; + } + +the_end: + *isamp = ibuf - istart; + *osamp = obuf - ostart; + rate->ilast = ilast; +} + +#undef NAME +#undef OP diff --git a/tools/ioemu/audio/sdlaudio.c b/tools/ioemu/audio/sdlaudio.c new file mode 100644 index 0000000000..713c7849d8 --- /dev/null +++ b/tools/ioemu/audio/sdlaudio.c @@ -0,0 +1,437 @@ +/* + * QEMU SDL audio driver + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include "vl.h" + +#define AUDIO_CAP "sdl" +#include "audio_int.h" + +typedef struct SDLVoiceOut { + HWVoiceOut hw; + int live; + int rpos; + int decr; +} SDLVoiceOut; + +static struct { + int nb_samples; +} conf = { + 1024 +}; + +struct SDLAudioState { + int exit; + SDL_mutex *mutex; + SDL_sem *sem; + int initialized; +} glob_sdl; +typedef struct SDLAudioState SDLAudioState; + +static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); +} + +static int sdl_lock (SDLAudioState *s, const char *forfn) +{ + if (SDL_LockMutex (s->mutex)) { + sdl_logerr ("SDL_LockMutex for %s failed\n", forfn); + return -1; + } + return 0; +} + +static int sdl_unlock (SDLAudioState *s, const char *forfn) +{ + if (SDL_UnlockMutex (s->mutex)) { + sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn); + return -1; + } + return 0; +} + +static int sdl_post (SDLAudioState *s, const char *forfn) +{ + if (SDL_SemPost (s->sem)) { + sdl_logerr ("SDL_SemPost for %s failed\n", forfn); + return -1; + } + return 0; +} + +static int sdl_wait (SDLAudioState *s, const char *forfn) +{ + if (SDL_SemWait (s->sem)) { + sdl_logerr ("SDL_SemWait for %s failed\n", forfn); + return -1; + } + return 0; +} + +static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) +{ + if (sdl_unlock (s, forfn)) { + return -1; + } + + return sdl_post (s, forfn); +} + +static int aud_to_sdlfmt (audfmt_e fmt, int *shift) +{ + switch (fmt) { + case AUD_FMT_S8: + *shift = 0; + return AUDIO_S8; + + case AUD_FMT_U8: + *shift = 0; + return AUDIO_U8; + + case AUD_FMT_S16: + *shift = 1; + return AUDIO_S16LSB; + + case AUD_FMT_U16: + *shift = 1; + return AUDIO_U16LSB; + + default: + dolog ("Internal logic error: Bad audio format %d\n", fmt); +#ifdef DEBUG_AUDIO + abort (); +#endif + return AUDIO_U8; + } +} + +static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess) +{ + switch (sdlfmt) { + case AUDIO_S8: + *endianess = 0; + *fmt = AUD_FMT_S8; + break; + + case AUDIO_U8: + *endianess = 0; + *fmt = AUD_FMT_U8; + break; + + case AUDIO_S16LSB: + *endianess = 0; + *fmt = AUD_FMT_S16; + break; + + case AUDIO_U16LSB: + *endianess = 0; + *fmt = AUD_FMT_U16; + break; + + case AUDIO_S16MSB: + *endianess = 1; + *fmt = AUD_FMT_S16; + break; + + case AUDIO_U16MSB: + *endianess = 1; + *fmt = AUD_FMT_U16; + break; + + default: + dolog ("Unrecognized SDL audio format %d\n", sdlfmt); + return -1; + } + + return 0; +} + +static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) +{ + int status; + + status = SDL_OpenAudio (req, obt); + if (status) { + sdl_logerr ("SDL_OpenAudio failed\n"); + } + return status; +} + +static void sdl_close (SDLAudioState *s) +{ + if (s->initialized) { + sdl_lock (s, "sdl_close"); + s->exit = 1; + sdl_unlock_and_post (s, "sdl_close"); + SDL_PauseAudio (1); + SDL_CloseAudio (); + s->initialized = 0; + } +} + +static void sdl_callback (void *opaque, Uint8 *buf, int len) +{ + SDLVoiceOut *sdl = opaque; + SDLAudioState *s = &glob_sdl; + HWVoiceOut *hw = &sdl->hw; + int samples = len >> hw->info.shift; + + if (s->exit) { + return; + } + + while (samples) { + int to_mix, decr; + + /* dolog ("in callback samples=%d\n", samples); */ + sdl_wait (s, "sdl_callback"); + if (s->exit) { + return; + } + + if (sdl_lock (s, "sdl_callback")) { + return; + } + + if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) { + dolog ("sdl->live=%d hw->samples=%d\n", + sdl->live, hw->samples); + return; + } + + if (!sdl->live) { + goto again; + } + + /* dolog ("in callback live=%d\n", live); */ + to_mix = audio_MIN (samples, sdl->live); + decr = to_mix; + while (to_mix) { + int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); + st_sample_t *src = hw->mix_buf + hw->rpos; + + /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ + hw->clip (buf, src, chunk); + mixeng_clear (src, chunk); + sdl->rpos = (sdl->rpos + chunk) % hw->samples; + to_mix -= chunk; + buf += chunk << hw->info.shift; + } + samples -= decr; + sdl->live -= decr; + sdl->decr += decr; + + again: + if (sdl_unlock (s, "sdl_callback")) { + return; + } + } + /* dolog ("done len=%d\n", len); */ +} + +static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int sdl_run_out (HWVoiceOut *hw) +{ + int decr, live; + SDLVoiceOut *sdl = (SDLVoiceOut *) hw; + SDLAudioState *s = &glob_sdl; + + if (sdl_lock (s, "sdl_callback")) { + return 0; + } + + live = audio_pcm_hw_get_live_out (hw); + + if (sdl->decr > live) { + ldebug ("sdl->decr %d live %d sdl->live %d\n", + sdl->decr, + live, + sdl->live); + } + + decr = audio_MIN (sdl->decr, live); + sdl->decr -= decr; + + sdl->live = live - decr; + hw->rpos = sdl->rpos; + + if (sdl->live > 0) { + sdl_unlock_and_post (s, "sdl_callback"); + } + else { + sdl_unlock (s, "sdl_callback"); + } + return decr; +} + +static void sdl_fini_out (HWVoiceOut *hw) +{ + (void) hw; + + sdl_close (&glob_sdl); +} + +static int sdl_init_out (HWVoiceOut *hw, audsettings_t *as) +{ + SDLVoiceOut *sdl = (SDLVoiceOut *) hw; + SDLAudioState *s = &glob_sdl; + SDL_AudioSpec req, obt; + int shift; + int endianess; + int err; + audfmt_e effective_fmt; + audsettings_t obt_as; + + shift <<= as->nchannels == 2; + + req.freq = as->freq; + req.format = aud_to_sdlfmt (as->fmt, &shift); + req.channels = as->nchannels; + req.samples = conf.nb_samples; + req.callback = sdl_callback; + req.userdata = sdl; + + if (sdl_open (&req, &obt)) { + return -1; + } + + err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess); + if (err) { + sdl_close (s); + return -1; + } + + obt_as.freq = obt.freq; + obt_as.nchannels = obt.channels; + obt_as.fmt = effective_fmt; + + audio_pcm_init_info ( + &hw->info, + &obt_as, + audio_need_to_swap_endian (endianess) + ); + hw->samples = obt.samples; + + s->initialized = 1; + s->exit = 0; + SDL_PauseAudio (0); + return 0; +} + +static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + (void) hw; + + switch (cmd) { + case VOICE_ENABLE: + SDL_PauseAudio (0); + break; + + case VOICE_DISABLE: + SDL_PauseAudio (1); + break; + } + return 0; +} + +static void *sdl_audio_init (void) +{ + SDLAudioState *s = &glob_sdl; + + if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { + sdl_logerr ("SDL failed to initialize audio subsystem\n"); + return NULL; + } + + s->mutex = SDL_CreateMutex (); + if (!s->mutex) { + sdl_logerr ("Failed to create SDL mutex\n"); + SDL_QuitSubSystem (SDL_INIT_AUDIO); + return NULL; + } + + s->sem = SDL_CreateSemaphore (0); + if (!s->sem) { + sdl_logerr ("Failed to create SDL semaphore\n"); + SDL_DestroyMutex (s->mutex); + SDL_QuitSubSystem (SDL_INIT_AUDIO); + return NULL; + } + + return s; +} + +static void sdl_audio_fini (void *opaque) +{ + SDLAudioState *s = opaque; + sdl_close (s); + SDL_DestroySemaphore (s->sem); + SDL_DestroyMutex (s->mutex); + SDL_QuitSubSystem (SDL_INIT_AUDIO); +} + +static struct audio_option sdl_options[] = { + {"SAMPLES", AUD_OPT_INT, &conf.nb_samples, + "Size of SDL buffer in samples", NULL, 0}, + {NULL, 0, NULL, NULL, NULL, 0} +}; + +static struct audio_pcm_ops sdl_pcm_ops = { + sdl_init_out, + sdl_fini_out, + sdl_run_out, + sdl_write_out, + sdl_ctl_out, + + NULL, + NULL, + NULL, + NULL, + NULL +}; + +struct audio_driver sdl_audio_driver = { + INIT_FIELD (name = ) "sdl", + INIT_FIELD (descr = ) "SDL http://www.libsdl.org", + INIT_FIELD (options = ) sdl_options, + INIT_FIELD (init = ) sdl_audio_init, + INIT_FIELD (fini = ) sdl_audio_fini, + INIT_FIELD (pcm_ops = ) &sdl_pcm_ops, + INIT_FIELD (can_be_default = ) 1, + INIT_FIELD (max_voices_out = ) 1, + INIT_FIELD (max_voices_in = ) 0, + INIT_FIELD (voice_size_out = ) sizeof (SDLVoiceOut), + INIT_FIELD (voice_size_in = ) 0 +}; diff --git a/tools/ioemu/audio/sys-queue.h b/tools/ioemu/audio/sys-queue.h new file mode 100644 index 0000000000..5b6e2a0a23 --- /dev/null +++ b/tools/ioemu/audio/sys-queue.h @@ -0,0 +1,241 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.3 (Berkeley) 12/13/93 + */ + +#ifndef _SYS_QUEUE_H +#define _SYS_QUEUE_H 1 + +/* + * This file defines three types of data structures: lists, tail queues, + * and circular queues. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list after + * an existing element or at the head of the list. A list may only be + * traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A tail queue may only be traversed in the forward direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#define LIST_INIT(head) { \ + (head)->lh_first = NULL; \ +} + +#define LIST_INSERT_AFTER(listelm, elm, field) { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} + +#define LIST_INSERT_HEAD(head, elm, field) { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} + +#define LIST_REMOVE(elm, field) { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} + +#define TAILQ_INSERT_HEAD(head, elm, field) { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} + +#define TAILQ_INSERT_TAIL(head, elm, field) { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} + +#define TAILQ_REMOVE(head, elm, field) { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} + +#define CIRCLEQ_REMOVE(head, elm, field) { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} +#endif /* sys/queue.h */ diff --git a/tools/ioemu/audio/wavaudio.c b/tools/ioemu/audio/wavaudio.c new file mode 100644 index 0000000000..18d2bb0c74 --- /dev/null +++ b/tools/ioemu/audio/wavaudio.c @@ -0,0 +1,255 @@ +/* + * QEMU WAV audio driver + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#define AUDIO_CAP "wav" +#include "audio_int.h" + +typedef struct WAVVoiceOut { + HWVoiceOut hw; + QEMUFile *f; + int64_t old_ticks; + void *pcm_buf; + int total_samples; +} WAVVoiceOut; + +static struct { + audsettings_t settings; + const char *wav_path; +} conf = { + { + 44100, + 2, + AUD_FMT_S16 + }, + "qemu.wav" +}; + +static int wav_run_out (HWVoiceOut *hw) +{ + WAVVoiceOut *wav = (WAVVoiceOut *) hw; + int rpos, live, decr, samples; + uint8_t *dst; + st_sample_t *src; + int64_t now = qemu_get_clock (vm_clock); + int64_t ticks = now - wav->old_ticks; + int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec; + + if (bytes > INT_MAX) { + samples = INT_MAX >> hw->info.shift; + } + else { + samples = bytes >> hw->info.shift; + } + + live = audio_pcm_hw_get_live_out (hw); + if (!live) { + return 0; + } + + wav->old_ticks = now; + decr = audio_MIN (live, samples); + samples = decr; + rpos = hw->rpos; + while (samples) { + int left_till_end_samples = hw->samples - rpos; + int convert_samples = audio_MIN (samples, left_till_end_samples); + + src = hw->mix_buf + rpos; + dst = advance (wav->pcm_buf, rpos << hw->info.shift); + + hw->clip (dst, src, convert_samples); + qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift); + mixeng_clear (src, convert_samples); + + rpos = (rpos + convert_samples) % hw->samples; + samples -= convert_samples; + wav->total_samples += convert_samples; + } + + hw->rpos = rpos; + return decr; +} + +static int wav_write_out (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +/* VICE code: Store number as little endian. */ +static void le_store (uint8_t *buf, uint32_t val, int len) +{ + int i; + for (i = 0; i < len; i++) { + buf[i] = (uint8_t) (val & 0xff); + val >>= 8; + } +} + +static int wav_init_out (HWVoiceOut *hw, audsettings_t *as) +{ + WAVVoiceOut *wav = (WAVVoiceOut *) hw; + int bits16 = 0, stereo = 0; + uint8_t hdr[] = { + 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, + 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, + 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 + }; + audsettings_t wav_as = conf.settings; + + (void) as; + + stereo = wav_as.nchannels == 2; + switch (wav_as.fmt) { + case AUD_FMT_S8: + case AUD_FMT_U8: + bits16 = 0; + break; + + case AUD_FMT_S16: + case AUD_FMT_U16: + bits16 = 1; + break; + } + + hdr[34] = bits16 ? 0x10 : 0x08; + + audio_pcm_init_info (&hw->info, &wav_as, audio_need_to_swap_endian (0)); + + hw->samples = 1024; + wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + if (!wav->pcm_buf) { + dolog ("Could not allocate buffer (%d bytes)\n", + hw->samples << hw->info.shift); + return -1; + } + + le_store (hdr + 22, hw->info.nchannels, 2); + le_store (hdr + 24, hw->info.freq, 4); + le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); + le_store (hdr + 32, 1 << (bits16 + stereo), 2); + + wav->f = fopen (conf.wav_path, "wb"); + if (!wav->f) { + dolog ("Failed to open wave file `%s'\nReason: %s\n", + conf.wav_path, strerror (errno)); + qemu_free (wav->pcm_buf); + wav->pcm_buf = NULL; + return -1; + } + + qemu_put_buffer (wav->f, hdr, sizeof (hdr)); + return 0; +} + +static void wav_fini_out (HWVoiceOut *hw) +{ + WAVVoiceOut *wav = (WAVVoiceOut *) hw; + uint8_t rlen[4]; + uint8_t dlen[4]; + uint32_t datalen = wav->total_samples << hw->info.shift; + uint32_t rifflen = datalen + 36; + + if (!wav->f) { + return; + } + + le_store (rlen, rifflen, 4); + le_store (dlen, datalen, 4); + + qemu_fseek (wav->f, 4, SEEK_SET); + qemu_put_buffer (wav->f, rlen, 4); + + qemu_fseek (wav->f, 32, SEEK_CUR); + qemu_put_buffer (wav->f, dlen, 4); + + fclose (wav->f); + wav->f = NULL; + + qemu_free (wav->pcm_buf); + wav->pcm_buf = NULL; +} + +static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + (void) hw; + (void) cmd; + return 0; +} + +static void *wav_audio_init (void) +{ + return &conf; +} + +static void wav_audio_fini (void *opaque) +{ + (void) opaque; + ldebug ("wav_fini"); +} + +struct audio_option wav_options[] = { + {"FREQUENCY", AUD_OPT_INT, &conf.settings.freq, + "Frequency", NULL, 0}, + + {"FORMAT", AUD_OPT_FMT, &conf.settings.fmt, + "Format", NULL, 0}, + + {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels, + "Number of channels (1 - mono, 2 - stereo)", NULL, 0}, + + {"PATH", AUD_OPT_STR, &conf.wav_path, + "Path to wave file", NULL, 0}, + {NULL, 0, NULL, NULL, NULL, 0} +}; + +struct audio_pcm_ops wav_pcm_ops = { + wav_init_out, + wav_fini_out, + wav_run_out, + wav_write_out, + wav_ctl_out, + + NULL, + NULL, + NULL, + NULL, + NULL +}; + +struct audio_driver wav_audio_driver = { + INIT_FIELD (name = ) "wav", + INIT_FIELD (descr = ) + "WAV renderer http://wikipedia.org/wiki/WAV", + INIT_FIELD (options = ) wav_options, + INIT_FIELD (init = ) wav_audio_init, + INIT_FIELD (fini = ) wav_audio_fini, + INIT_FIELD (pcm_ops = ) &wav_pcm_ops, + INIT_FIELD (can_be_default = ) 0, + INIT_FIELD (max_voices_out = ) 1, + INIT_FIELD (max_voices_in = ) 0, + INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut), + INIT_FIELD (voice_size_in = ) 0 +}; diff --git a/tools/ioemu/block-bochs.c b/tools/ioemu/block-bochs.c new file mode 100644 index 0000000000..62317aff38 --- /dev/null +++ b/tools/ioemu/block-bochs.c @@ -0,0 +1,224 @@ +/* + * Block driver for the various disk image formats used by Bochs + * Currently only for "growing" type in read-only mode + * + * Copyright (c) 2005 Alex Beregszaszi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "block_int.h" + +/**************************************************************/ + +#define HEADER_MAGIC "Bochs Virtual HD Image" +#define HEADER_VERSION 0x00010000 +#define HEADER_SIZE 512 + +#define REDOLOG_TYPE "Redolog" +#define GROWING_TYPE "Growing" + +// not allocated: 0xffffffff + +// always little-endian +struct bochs_header { + char magic[32]; // "Bochs Virtual HD Image" + char type[16]; // "Redolog" + char subtype[16]; // "Undoable" / "Volatile" / "Growing" + uint32_t version; + uint32_t header; // size of header + + union { + struct { + uint32_t catalog; // num of entries + uint32_t bitmap; // bitmap size + uint32_t extent; // extent size + uint64_t disk; // disk size + char padding[HEADER_SIZE - 64 - 8 - 20]; + } redolog; + char padding[HEADER_SIZE - 64 - 8]; + } extra; +}; + +typedef struct BDRVBochsState { + int fd; + + uint32_t *catalog_bitmap; + int catalog_size; + + int data_offset; + + int bitmap_blocks; + int extent_blocks; + int extent_size; +} BDRVBochsState; + +static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + const struct bochs_header *bochs = (const void *)buf; + + if (buf_size < HEADER_SIZE) + return 0; + + if (!strcmp(bochs->magic, HEADER_MAGIC) && + !strcmp(bochs->type, REDOLOG_TYPE) && + !strcmp(bochs->subtype, GROWING_TYPE) && + (le32_to_cpu(bochs->version) == HEADER_VERSION)) + return 100; + + return 0; +} + +static int bochs_open(BlockDriverState *bs, const char *filename) +{ + BDRVBochsState *s = bs->opaque; + int fd, i; + struct bochs_header bochs; + + fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); + if (fd < 0) { + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + if (fd < 0) + return -1; + } + + bs->read_only = 1; // no write support yet + + s->fd = fd; + + if (read(fd, &bochs, sizeof(bochs)) != sizeof(bochs)) { + goto fail; + } + + if (strcmp(bochs.magic, HEADER_MAGIC) || + strcmp(bochs.type, REDOLOG_TYPE) || + strcmp(bochs.subtype, GROWING_TYPE) || + (le32_to_cpu(bochs.version) != HEADER_VERSION)) { + goto fail; + } + + bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512; + + lseek(s->fd, le32_to_cpu(bochs.header), SEEK_SET); + + s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog); + s->catalog_bitmap = qemu_malloc(s->catalog_size * 4); + if (!s->catalog_bitmap) + goto fail; + if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) != + s->catalog_size * 4) + goto fail; + for (i = 0; i < s->catalog_size; i++) + le32_to_cpus(&s->catalog_bitmap[i]); + + s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4); + + s->bitmap_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.bitmap) - 1) / 512; + s->extent_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.extent) - 1) / 512; + + s->extent_size = le32_to_cpu(bochs.extra.redolog.extent); + + return 0; + fail: + close(fd); + return -1; +} + +static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num) +{ + BDRVBochsState *s = bs->opaque; + int64_t offset = sector_num * 512; + int64_t extent_index, extent_offset, bitmap_offset, block_offset; + char bitmap_entry; + + // seek to sector + extent_index = offset / s->extent_size; + extent_offset = (offset % s->extent_size) / 512; + + if (s->catalog_bitmap[extent_index] == 0xffffffff) + { +// fprintf(stderr, "page not allocated [%x - %x:%x]\n", +// sector_num, extent_index, extent_offset); + return -1; // not allocated + } + + bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] * + (s->extent_blocks + s->bitmap_blocks)); + block_offset = bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); + +// fprintf(stderr, "sect: %x [ext i: %x o: %x] -> %x bitmap: %x block: %x\n", +// sector_num, extent_index, extent_offset, +// le32_to_cpu(s->catalog_bitmap[extent_index]), +// bitmap_offset, block_offset); + + // read in bitmap for current extent + lseek(s->fd, bitmap_offset + (extent_offset / 8), SEEK_SET); + + read(s->fd, &bitmap_entry, 1); + + if (!((bitmap_entry >> (extent_offset % 8)) & 1)) + { +// fprintf(stderr, "sector (%x) in bitmap not allocated\n", +// sector_num); + return -1; // not allocated + } + + lseek(s->fd, block_offset, SEEK_SET); + + return 0; +} + +static int bochs_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVBochsState *s = bs->opaque; + int ret; + + while (nb_sectors > 0) { + if (!seek_to_sector(bs, sector_num)) + { + ret = read(s->fd, buf, 512); + if (ret != 512) + return -1; + } + else + memset(buf, 0, 512); + nb_sectors--; + sector_num++; + buf += 512; + } + return 0; +} + +static void bochs_close(BlockDriverState *bs) +{ + BDRVBochsState *s = bs->opaque; + qemu_free(s->catalog_bitmap); + close(s->fd); +} + +BlockDriver bdrv_bochs = { + "bochs", + sizeof(BDRVBochsState), + bochs_probe, + bochs_open, + bochs_read, + NULL, + bochs_close, +}; diff --git a/tools/ioemu/block-cloop.c b/tools/ioemu/block-cloop.c new file mode 100644 index 0000000000..c617e1b64b --- /dev/null +++ b/tools/ioemu/block-cloop.c @@ -0,0 +1,169 @@ +/* + * QEMU Block driver for CLOOP images + * + * Copyright (c) 2004 Johannes E. Schindelin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "block_int.h" +#include + +typedef struct BDRVCloopState { + int fd; + uint32_t block_size; + uint32_t n_blocks; + uint64_t* offsets; + uint32_t sectors_per_block; + uint32_t current_block; + uint8_t *compressed_block; + uint8_t *uncompressed_block; + z_stream zstream; +} BDRVCloopState; + +static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + const char* magic_version_2_0="#!/bin/sh\n" + "#V2.0 Format\n" + "modprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n"; + int length=strlen(magic_version_2_0); + if(length>buf_size) + length=buf_size; + if(!memcmp(magic_version_2_0,buf,length)) + return 2; + return 0; +} + +static int cloop_open(BlockDriverState *bs, const char *filename) +{ + BDRVCloopState *s = bs->opaque; + uint32_t offsets_size,max_compressed_block_size=1,i; + + s->fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + if (s->fd < 0) + return -1; + bs->read_only = 1; + + /* read header */ + if(lseek(s->fd,128,SEEK_SET)<0) { +cloop_close: + close(s->fd); + return -1; + } + if(read(s->fd,&s->block_size,4)<4) + goto cloop_close; + s->block_size=be32_to_cpu(s->block_size); + if(read(s->fd,&s->n_blocks,4)<4) + goto cloop_close; + s->n_blocks=be32_to_cpu(s->n_blocks); + + /* read offsets */ + offsets_size=s->n_blocks*sizeof(uint64_t); + if(!(s->offsets=(uint64_t*)malloc(offsets_size))) + goto cloop_close; + if(read(s->fd,s->offsets,offsets_size)n_blocks;i++) { + s->offsets[i]=be64_to_cpu(s->offsets[i]); + if(i>0) { + uint32_t size=s->offsets[i]-s->offsets[i-1]; + if(size>max_compressed_block_size) + max_compressed_block_size=size; + } + } + + /* initialize zlib engine */ + if(!(s->compressed_block = malloc(max_compressed_block_size+1))) + goto cloop_close; + if(!(s->uncompressed_block = malloc(s->block_size))) + goto cloop_close; + if(inflateInit(&s->zstream) != Z_OK) + goto cloop_close; + s->current_block=s->n_blocks; + + s->sectors_per_block = s->block_size/512; + bs->total_sectors = s->n_blocks*s->sectors_per_block; + return 0; +} + +static inline int cloop_read_block(BDRVCloopState *s,int block_num) +{ + if(s->current_block != block_num) { + int ret; + uint32_t bytes = s->offsets[block_num+1]-s->offsets[block_num]; + + lseek(s->fd, s->offsets[block_num], SEEK_SET); + ret = read(s->fd, s->compressed_block, bytes); + if (ret != bytes) + return -1; + + s->zstream.next_in = s->compressed_block; + s->zstream.avail_in = bytes; + s->zstream.next_out = s->uncompressed_block; + s->zstream.avail_out = s->block_size; + ret = inflateReset(&s->zstream); + if(ret != Z_OK) + return -1; + ret = inflate(&s->zstream, Z_FINISH); + if(ret != Z_STREAM_END || s->zstream.total_out != s->block_size) + return -1; + + s->current_block = block_num; + } + return 0; +} + +static int cloop_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVCloopState *s = bs->opaque; + int i; + + for(i=0;isectors_per_block), + block_num=(sector_num+i)/s->sectors_per_block; + if(cloop_read_block(s, block_num) != 0) + return -1; + memcpy(buf+i*512,s->uncompressed_block+sector_offset_in_block*512,512); + } + return 0; +} + +static void cloop_close(BlockDriverState *bs) +{ + BDRVCloopState *s = bs->opaque; + close(s->fd); + if(s->n_blocks>0) + free(s->offsets); + free(s->compressed_block); + free(s->uncompressed_block); + inflateEnd(&s->zstream); +} + +BlockDriver bdrv_cloop = { + "cloop", + sizeof(BDRVCloopState), + cloop_probe, + cloop_open, + cloop_read, + NULL, + cloop_close, +}; + + diff --git a/tools/ioemu/block-cow.c b/tools/ioemu/block-cow.c new file mode 100644 index 0000000000..eeeab7068b --- /dev/null +++ b/tools/ioemu/block-cow.c @@ -0,0 +1,264 @@ +/* + * Block driver for the COW format + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef _WIN32 +#include "vl.h" +#include "block_int.h" +#include + +/**************************************************************/ +/* COW block driver using file system holes */ + +/* user mode linux compatible COW file */ +#define COW_MAGIC 0x4f4f4f4d /* MOOO */ +#define COW_VERSION 2 + +struct cow_header_v2 { + uint32_t magic; + uint32_t version; + char backing_file[1024]; + int32_t mtime; + uint64_t size; + uint32_t sectorsize; +}; + +typedef struct BDRVCowState { + int fd; + uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */ + uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */ + int cow_bitmap_size; + int64_t cow_sectors_offset; +} BDRVCowState; + +static int cow_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + const struct cow_header_v2 *cow_header = (const void *)buf; + + if (buf_size >= sizeof(struct cow_header_v2) && + be32_to_cpu(cow_header->magic) == COW_MAGIC && + be32_to_cpu(cow_header->version) == COW_VERSION) + return 100; + else + return 0; +} + +static int cow_open(BlockDriverState *bs, const char *filename) +{ + BDRVCowState *s = bs->opaque; + int fd; + struct cow_header_v2 cow_header; + int64_t size; + + fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); + if (fd < 0) { + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + if (fd < 0) + return -1; + } + s->fd = fd; + /* see if it is a cow image */ + if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) { + goto fail; + } + + if (be32_to_cpu(cow_header.magic) != COW_MAGIC || + be32_to_cpu(cow_header.version) != COW_VERSION) { + goto fail; + } + + /* cow image found */ + size = be64_to_cpu(cow_header.size); + bs->total_sectors = size / 512; + + pstrcpy(bs->backing_file, sizeof(bs->backing_file), + cow_header.backing_file); + +#if 0 + if (cow_header.backing_file[0] != '\0') { + if (stat(cow_header.backing_file, &st) != 0) { + fprintf(stderr, "%s: could not find original disk image '%s'\n", filename, cow_header.backing_file); + goto fail; + } + if (st.st_mtime != be32_to_cpu(cow_header.mtime)) { + fprintf(stderr, "%s: original raw disk image '%s' does not match saved timestamp\n", filename, cow_header.backing_file); + goto fail; + } + fd = open(cow_header.backing_file, O_RDONLY | O_LARGEFILE); + if (fd < 0) + goto fail; + bs->fd = fd; + } +#endif + /* mmap the bitmap */ + s->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); + s->cow_bitmap_addr = mmap(get_mmap_addr(s->cow_bitmap_size), + s->cow_bitmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, s->fd, 0); + if (s->cow_bitmap_addr == MAP_FAILED) + goto fail; + s->cow_bitmap = s->cow_bitmap_addr + sizeof(cow_header); + s->cow_sectors_offset = (s->cow_bitmap_size + 511) & ~511; + return 0; + fail: + close(fd); + return -1; +} + +static inline void cow_set_bit(uint8_t *bitmap, int64_t bitnum) +{ + bitmap[bitnum / 8] |= (1 << (bitnum%8)); +} + +static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum) +{ + return !!(bitmap[bitnum / 8] & (1 << (bitnum%8))); +} + + +/* Return true if first block has been changed (ie. current version is + * in COW file). Set the number of continuous blocks for which that + * is true. */ +static inline int is_changed(uint8_t *bitmap, + int64_t sector_num, int nb_sectors, + int *num_same) +{ + int changed; + + if (!bitmap || nb_sectors == 0) { + *num_same = nb_sectors; + return 0; + } + + changed = is_bit_set(bitmap, sector_num); + for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) { + if (is_bit_set(bitmap, sector_num + *num_same) != changed) + break; + } + + return changed; +} + +static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) +{ + BDRVCowState *s = bs->opaque; + return is_changed(s->cow_bitmap, sector_num, nb_sectors, pnum); +} + +static int cow_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVCowState *s = bs->opaque; + int ret, n; + + while (nb_sectors > 0) { + if (is_changed(s->cow_bitmap, sector_num, nb_sectors, &n)) { + lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET); + ret = read(s->fd, buf, n * 512); + if (ret != n * 512) + return -1; + } else { + memset(buf, 0, n * 512); + } + nb_sectors -= n; + sector_num += n; + buf += n * 512; + } + return 0; +} + +static int cow_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BDRVCowState *s = bs->opaque; + int ret, i; + + lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET); + ret = write(s->fd, buf, nb_sectors * 512); + if (ret != nb_sectors * 512) + return -1; + for (i = 0; i < nb_sectors; i++) + cow_set_bit(s->cow_bitmap, sector_num + i); + return 0; +} + +static void cow_close(BlockDriverState *bs) +{ + BDRVCowState *s = bs->opaque; + munmap(s->cow_bitmap_addr, s->cow_bitmap_size); + close(s->fd); +} + +static int cow_create(const char *filename, int64_t image_sectors, + const char *image_filename, int flags) +{ + int fd, cow_fd; + struct cow_header_v2 cow_header; + struct stat st; + + if (flags) + return -ENOTSUP; + + cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, + 0644); + if (cow_fd < 0) + return -1; + memset(&cow_header, 0, sizeof(cow_header)); + cow_header.magic = cpu_to_be32(COW_MAGIC); + cow_header.version = cpu_to_be32(COW_VERSION); + if (image_filename) { + fd = open(image_filename, O_RDONLY | O_BINARY); + if (fd < 0) { + close(cow_fd); + return -1; + } + if (fstat(fd, &st) != 0) { + close(fd); + return -1; + } + close(fd); + cow_header.mtime = cpu_to_be32(st.st_mtime); + realpath(image_filename, cow_header.backing_file); + } + cow_header.sectorsize = cpu_to_be32(512); + cow_header.size = cpu_to_be64(image_sectors * 512); + write(cow_fd, &cow_header, sizeof(cow_header)); + /* resize to include at least all the bitmap */ + ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3)); + close(cow_fd); + return 0; +} + +BlockDriver bdrv_cow = { + "cow", + sizeof(BDRVCowState), + cow_probe, + cow_open, + cow_read, + cow_write, + cow_close, + cow_create, + cow_is_allocated, +}; +#endif diff --git a/tools/ioemu/block-dmg.c b/tools/ioemu/block-dmg.c new file mode 100644 index 0000000000..a16ab926b5 --- /dev/null +++ b/tools/ioemu/block-dmg.c @@ -0,0 +1,297 @@ +/* + * QEMU Block driver for DMG images + * + * Copyright (c) 2004 Johannes E. Schindelin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "block_int.h" +#include "bswap.h" +#include + +typedef struct BDRVDMGState { + int fd; + + /* each chunk contains a certain number of sectors, + * offsets[i] is the offset in the .dmg file, + * lengths[i] is the length of the compressed chunk, + * sectors[i] is the sector beginning at offsets[i], + * sectorcounts[i] is the number of sectors in that chunk, + * the sectors array is ordered + * 0<=i4 && !strcmp(filename+len-4,".dmg")) + return 2; + return 0; +} + +static off_t read_off(int fd) +{ + uint64_t buffer; + if(read(fd,&buffer,8)<8) + return 0; + return be64_to_cpu(buffer); +} + +static off_t read_uint32(int fd) +{ + uint32_t buffer; + if(read(fd,&buffer,4)<4) + return 0; + return be32_to_cpu(buffer); +} + +static int dmg_open(BlockDriverState *bs, const char *filename) +{ + BDRVDMGState *s = bs->opaque; + off_t info_begin,info_end,last_in_offset,last_out_offset; + uint32_t count; + uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i; + + s->fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + if (s->fd < 0) + return -1; + bs->read_only = 1; + s->n_chunks = 0; + s->offsets = s->lengths = s->sectors = s->sectorcounts = 0; + + /* read offset of info blocks */ + if(lseek(s->fd,-0x1d8,SEEK_END)<0) { +dmg_close: + close(s->fd); + /* open raw instead */ + bs->drv=&bdrv_raw; + return bs->drv->bdrv_open(bs,filename); + } + info_begin=read_off(s->fd); + if(info_begin==0) + goto dmg_close; + if(lseek(s->fd,info_begin,SEEK_SET)<0) + goto dmg_close; + if(read_uint32(s->fd)!=0x100) + goto dmg_close; + if((count = read_uint32(s->fd))==0) + goto dmg_close; + info_end = info_begin+count; + if(lseek(s->fd,0xf8,SEEK_CUR)<0) + goto dmg_close; + + /* read offsets */ + last_in_offset = last_out_offset = 0; + while(lseek(s->fd,0,SEEK_CUR)fd); + if(count==0) + goto dmg_close; + type = read_uint32(s->fd); + if(type!=0x6d697368 || count<244) + lseek(s->fd,count-4,SEEK_CUR); + else { + int new_size, chunk_count; + if(lseek(s->fd,200,SEEK_CUR)<0) + goto dmg_close; + chunk_count = (count-204)/40; + new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count); + s->types = realloc(s->types, new_size/2); + s->offsets = realloc(s->offsets, new_size); + s->lengths = realloc(s->lengths, new_size); + s->sectors = realloc(s->sectors, new_size); + s->sectorcounts = realloc(s->sectorcounts, new_size); + + for(i=s->n_chunks;in_chunks+chunk_count;i++) { + s->types[i] = read_uint32(s->fd); + if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) { + if(s->types[i]==0xffffffff) { + last_in_offset = s->offsets[i-1]+s->lengths[i-1]; + last_out_offset = s->sectors[i-1]+s->sectorcounts[i-1]; + } + chunk_count--; + i--; + if(lseek(s->fd,36,SEEK_CUR)<0) + goto dmg_close; + continue; + } + read_uint32(s->fd); + s->sectors[i] = last_out_offset+read_off(s->fd); + s->sectorcounts[i] = read_off(s->fd); + s->offsets[i] = last_in_offset+read_off(s->fd); + s->lengths[i] = read_off(s->fd); + if(s->lengths[i]>max_compressed_size) + max_compressed_size = s->lengths[i]; + if(s->sectorcounts[i]>max_sectors_per_chunk) + max_sectors_per_chunk = s->sectorcounts[i]; + } + s->n_chunks+=chunk_count; + } + } + + /* initialize zlib engine */ + if(!(s->compressed_chunk = malloc(max_compressed_size+1))) + goto dmg_close; + if(!(s->uncompressed_chunk = malloc(512*max_sectors_per_chunk))) + goto dmg_close; + if(inflateInit(&s->zstream) != Z_OK) + goto dmg_close; + + s->current_chunk = s->n_chunks; + + return 0; +} + +static inline int is_sector_in_chunk(BDRVDMGState* s, + uint32_t chunk_num,int sector_num) +{ + if(chunk_num>=s->n_chunks || s->sectors[chunk_num]>sector_num || + s->sectors[chunk_num]+s->sectorcounts[chunk_num]<=sector_num) + return 0; + else + return -1; +} + +static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num) +{ + /* binary search */ + uint32_t chunk1=0,chunk2=s->n_chunks,chunk3; + while(chunk1!=chunk2) { + chunk3 = (chunk1+chunk2)/2; + if(s->sectors[chunk3]>sector_num) + chunk2 = chunk3; + else if(s->sectors[chunk3]+s->sectorcounts[chunk3]>sector_num) + return chunk3; + else + chunk1 = chunk3; + } + return s->n_chunks; /* error */ +} + +static inline int dmg_read_chunk(BDRVDMGState *s,int sector_num) +{ + if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) { + int ret; + uint32_t chunk = search_chunk(s,sector_num); + + if(chunk>=s->n_chunks) + return -1; + + s->current_chunk = s->n_chunks; + switch(s->types[chunk]) { + case 0x80000005: { /* zlib compressed */ + int i; + + ret = lseek(s->fd, s->offsets[chunk], SEEK_SET); + if(ret<0) + return -1; + + /* we need to buffer, because only the chunk as whole can be + * inflated. */ + i=0; + do { + ret = read(s->fd, s->compressed_chunk+i, s->lengths[chunk]-i); + if(ret<0 && errno==EINTR) + ret=0; + i+=ret; + } while(ret>=0 && ret+ilengths[chunk]); + + if (ret != s->lengths[chunk]) + return -1; + + s->zstream.next_in = s->compressed_chunk; + s->zstream.avail_in = s->lengths[chunk]; + s->zstream.next_out = s->uncompressed_chunk; + s->zstream.avail_out = 512*s->sectorcounts[chunk]; + ret = inflateReset(&s->zstream); + if(ret != Z_OK) + return -1; + ret = inflate(&s->zstream, Z_FINISH); + if(ret != Z_STREAM_END || s->zstream.total_out != 512*s->sectorcounts[chunk]) + return -1; + break; } + case 1: /* copy */ + ret = read(s->fd, s->uncompressed_chunk, s->lengths[chunk]); + if (ret != s->lengths[chunk]) + return -1; + break; + case 2: /* zero */ + memset(s->uncompressed_chunk, 0, 512*s->sectorcounts[chunk]); + break; + } + s->current_chunk = chunk; + } + return 0; +} + +static int dmg_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVDMGState *s = bs->opaque; + int i; + + for(i=0;isectors[s->current_chunk]; + memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512); + } + return 0; +} + +static void dmg_close(BlockDriverState *bs) +{ + BDRVDMGState *s = bs->opaque; + close(s->fd); + if(s->n_chunks>0) { + free(s->types); + free(s->offsets); + free(s->lengths); + free(s->sectors); + free(s->sectorcounts); + } + free(s->compressed_chunk); + free(s->uncompressed_chunk); + inflateEnd(&s->zstream); +} + +BlockDriver bdrv_dmg = { + "dmg", + sizeof(BDRVDMGState), + dmg_probe, + dmg_open, + dmg_read, + NULL, + dmg_close, +}; + diff --git a/tools/ioemu/block-qcow.c b/tools/ioemu/block-qcow.c new file mode 100644 index 0000000000..34026a4f2c --- /dev/null +++ b/tools/ioemu/block-qcow.c @@ -0,0 +1,710 @@ +/* + * Block driver for the QCOW format + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "block_int.h" +#include +#include "aes.h" + +/**************************************************************/ +/* QEMU COW block driver with compression and encryption support */ + +#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) +#define QCOW_VERSION 1 + +#define QCOW_CRYPT_NONE 0 +#define QCOW_CRYPT_AES 1 + +#define QCOW_OFLAG_COMPRESSED (1LL << 63) + +typedef struct QCowHeader { + uint32_t magic; + uint32_t version; + uint64_t backing_file_offset; + uint32_t backing_file_size; + uint32_t mtime; + uint64_t size; /* in bytes */ + uint8_t cluster_bits; + uint8_t l2_bits; + uint32_t crypt_method; + uint64_t l1_table_offset; +} QCowHeader; + +#define L2_CACHE_SIZE 16 + +typedef struct BDRVQcowState { + int fd; + int cluster_bits; + int cluster_size; + int cluster_sectors; + int l2_bits; + int l2_size; + int l1_size; + uint64_t cluster_offset_mask; + uint64_t l1_table_offset; + uint64_t *l1_table; + uint64_t *l2_cache; + uint64_t l2_cache_offsets[L2_CACHE_SIZE]; + uint32_t l2_cache_counts[L2_CACHE_SIZE]; + uint8_t *cluster_cache; + uint8_t *cluster_data; + uint64_t cluster_cache_offset; + uint32_t crypt_method; /* current crypt method, 0 if no key yet */ + uint32_t crypt_method_header; + AES_KEY aes_encrypt_key; + AES_KEY aes_decrypt_key; +} BDRVQcowState; + +static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset); + +static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + const QCowHeader *cow_header = (const void *)buf; + + if (buf_size >= sizeof(QCowHeader) && + be32_to_cpu(cow_header->magic) == QCOW_MAGIC && + be32_to_cpu(cow_header->version) == QCOW_VERSION) + return 100; + else + return 0; +} + +static int qcow_open(BlockDriverState *bs, const char *filename) +{ + BDRVQcowState *s = bs->opaque; + int fd, len, i, shift; + QCowHeader header; + + fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); + if (fd < 0) { + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + if (fd < 0) + return -1; + } + s->fd = fd; + if (read(fd, &header, sizeof(header)) != sizeof(header)) + goto fail; + be32_to_cpus(&header.magic); + be32_to_cpus(&header.version); + be64_to_cpus(&header.backing_file_offset); + be32_to_cpus(&header.backing_file_size); + be32_to_cpus(&header.mtime); + be64_to_cpus(&header.size); + be32_to_cpus(&header.crypt_method); + be64_to_cpus(&header.l1_table_offset); + + if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION) + goto fail; + if (header.size <= 1 || header.cluster_bits < 9) + goto fail; + if (header.crypt_method > QCOW_CRYPT_AES) + goto fail; + s->crypt_method_header = header.crypt_method; + if (s->crypt_method_header) + bs->encrypted = 1; + s->cluster_bits = header.cluster_bits; + s->cluster_size = 1 << s->cluster_bits; + s->cluster_sectors = 1 << (s->cluster_bits - 9); + s->l2_bits = header.l2_bits; + s->l2_size = 1 << s->l2_bits; + bs->total_sectors = header.size / 512; + s->cluster_offset_mask = (1LL << (63 - s->cluster_bits)) - 1; + + /* read the level 1 table */ + shift = s->cluster_bits + s->l2_bits; + s->l1_size = (header.size + (1LL << shift) - 1) >> shift; + + s->l1_table_offset = header.l1_table_offset; + s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t)); + if (!s->l1_table) + goto fail; + lseek(fd, s->l1_table_offset, SEEK_SET); + if (read(fd, s->l1_table, s->l1_size * sizeof(uint64_t)) != + s->l1_size * sizeof(uint64_t)) + goto fail; + for(i = 0;i < s->l1_size; i++) { + be64_to_cpus(&s->l1_table[i]); + } + /* alloc L2 cache */ + s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); + if (!s->l2_cache) + goto fail; + s->cluster_cache = qemu_malloc(s->cluster_size); + if (!s->cluster_cache) + goto fail; + s->cluster_data = qemu_malloc(s->cluster_size); + if (!s->cluster_data) + goto fail; + s->cluster_cache_offset = -1; + + /* read the backing file name */ + if (header.backing_file_offset != 0) { + len = header.backing_file_size; + if (len > 1023) + len = 1023; + lseek(fd, header.backing_file_offset, SEEK_SET); + if (read(fd, bs->backing_file, len) != len) + goto fail; + bs->backing_file[len] = '\0'; + } + return 0; + + fail: + qemu_free(s->l1_table); + qemu_free(s->l2_cache); + qemu_free(s->cluster_cache); + qemu_free(s->cluster_data); + close(fd); + return -1; +} + +static int qcow_set_key(BlockDriverState *bs, const char *key) +{ + BDRVQcowState *s = bs->opaque; + uint8_t keybuf[16]; + int len, i; + + memset(keybuf, 0, 16); + len = strlen(key); + if (len > 16) + len = 16; + /* XXX: we could compress the chars to 7 bits to increase + entropy */ + for(i = 0;i < len;i++) { + keybuf[i] = key[i]; + } + s->crypt_method = s->crypt_method_header; + + if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) + return -1; + if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0) + return -1; +#if 0 + /* test */ + { + uint8_t in[16]; + uint8_t out[16]; + uint8_t tmp[16]; + for(i=0;i<16;i++) + in[i] = i; + AES_encrypt(in, tmp, &s->aes_encrypt_key); + AES_decrypt(tmp, out, &s->aes_decrypt_key); + for(i = 0; i < 16; i++) + printf(" %02x", tmp[i]); + printf("\n"); + for(i = 0; i < 16; i++) + printf(" %02x", out[i]); + printf("\n"); + } +#endif + return 0; +} + +/* The crypt function is compatible with the linux cryptoloop + algorithm for < 4 GB images. NOTE: out_buf == in_buf is + supported */ +static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num, + uint8_t *out_buf, const uint8_t *in_buf, + int nb_sectors, int enc, + const AES_KEY *key) +{ + union { + uint64_t ll[2]; + uint8_t b[16]; + } ivec; + int i; + + for(i = 0; i < nb_sectors; i++) { + ivec.ll[0] = cpu_to_le64(sector_num); + ivec.ll[1] = 0; + AES_cbc_encrypt(in_buf, out_buf, 512, key, + ivec.b, enc); + sector_num++; + in_buf += 512; + out_buf += 512; + } +} + +/* 'allocate' is: + * + * 0 to not allocate. + * + * 1 to allocate a normal cluster (for sector indexes 'n_start' to + * 'n_end') + * + * 2 to allocate a compressed cluster of size + * 'compressed_size'. 'compressed_size' must be > 0 and < + * cluster_size + * + * return 0 if not allocated. + */ +static uint64_t get_cluster_offset(BlockDriverState *bs, + uint64_t offset, int allocate, + int compressed_size, + int n_start, int n_end) +{ + BDRVQcowState *s = bs->opaque; + int min_index, i, j, l1_index, l2_index; + uint64_t l2_offset, *l2_table, cluster_offset, tmp; + uint32_t min_count; + int new_l2_table; + + l1_index = offset >> (s->l2_bits + s->cluster_bits); + l2_offset = s->l1_table[l1_index]; + new_l2_table = 0; + if (!l2_offset) { + if (!allocate) + return 0; + /* allocate a new l2 entry */ + l2_offset = lseek(s->fd, 0, SEEK_END); + /* round to cluster size */ + l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); + /* update the L1 entry */ + s->l1_table[l1_index] = l2_offset; + tmp = cpu_to_be64(l2_offset); + lseek(s->fd, s->l1_table_offset + l1_index * sizeof(tmp), SEEK_SET); + if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + return 0; + new_l2_table = 1; + } + for(i = 0; i < L2_CACHE_SIZE; i++) { + if (l2_offset == s->l2_cache_offsets[i]) { + /* increment the hit count */ + if (++s->l2_cache_counts[i] == 0xffffffff) { + for(j = 0; j < L2_CACHE_SIZE; j++) { + s->l2_cache_counts[j] >>= 1; + } + } + l2_table = s->l2_cache + (i << s->l2_bits); + goto found; + } + } + /* not found: load a new entry in the least used one */ + min_index = 0; + min_count = 0xffffffff; + for(i = 0; i < L2_CACHE_SIZE; i++) { + if (s->l2_cache_counts[i] < min_count) { + min_count = s->l2_cache_counts[i]; + min_index = i; + } + } + l2_table = s->l2_cache + (min_index << s->l2_bits); + lseek(s->fd, l2_offset, SEEK_SET); + if (new_l2_table) { + memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); + if (write(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) != + s->l2_size * sizeof(uint64_t)) + return 0; + } else { + if (read(s->fd, l2_table, s->l2_size * sizeof(uint64_t)) != + s->l2_size * sizeof(uint64_t)) + return 0; + } + s->l2_cache_offsets[min_index] = l2_offset; + s->l2_cache_counts[min_index] = 1; + found: + l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); + cluster_offset = be64_to_cpu(l2_table[l2_index]); + if (!cluster_offset || + ((cluster_offset & QCOW_OFLAG_COMPRESSED) && allocate == 1)) { + if (!allocate) + return 0; + /* allocate a new cluster */ + if ((cluster_offset & QCOW_OFLAG_COMPRESSED) && + (n_end - n_start) < s->cluster_sectors) { + /* if the cluster is already compressed, we must + decompress it in the case it is not completely + overwritten */ + if (decompress_cluster(s, cluster_offset) < 0) + return 0; + cluster_offset = lseek(s->fd, 0, SEEK_END); + cluster_offset = (cluster_offset + s->cluster_size - 1) & + ~(s->cluster_size - 1); + /* write the cluster content */ + lseek(s->fd, cluster_offset, SEEK_SET); + if (write(s->fd, s->cluster_cache, s->cluster_size) != + s->cluster_size) + return -1; + } else { + cluster_offset = lseek(s->fd, 0, SEEK_END); + if (allocate == 1) { + /* round to cluster size */ + cluster_offset = (cluster_offset + s->cluster_size - 1) & + ~(s->cluster_size - 1); + ftruncate(s->fd, cluster_offset + s->cluster_size); + /* if encrypted, we must initialize the cluster + content which won't be written */ + if (s->crypt_method && + (n_end - n_start) < s->cluster_sectors) { + uint64_t start_sect; + start_sect = (offset & ~(s->cluster_size - 1)) >> 9; + memset(s->cluster_data + 512, 0xaa, 512); + for(i = 0; i < s->cluster_sectors; i++) { + if (i < n_start || i >= n_end) { + encrypt_sectors(s, start_sect + i, + s->cluster_data, + s->cluster_data + 512, 1, 1, + &s->aes_encrypt_key); + lseek(s->fd, cluster_offset + i * 512, SEEK_SET); + if (write(s->fd, s->cluster_data, 512) != 512) + return -1; + } + } + } + } else { + cluster_offset |= QCOW_OFLAG_COMPRESSED | + (uint64_t)compressed_size << (63 - s->cluster_bits); + } + } + /* update L2 table */ + tmp = cpu_to_be64(cluster_offset); + l2_table[l2_index] = tmp; + lseek(s->fd, l2_offset + l2_index * sizeof(tmp), SEEK_SET); + if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + return 0; + } + return cluster_offset; +} + +static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) +{ + BDRVQcowState *s = bs->opaque; + int index_in_cluster, n; + uint64_t cluster_offset; + + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0); + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + *pnum = n; + return (cluster_offset != 0); +} + +static int decompress_buffer(uint8_t *out_buf, int out_buf_size, + const uint8_t *buf, int buf_size) +{ + z_stream strm1, *strm = &strm1; + int ret, out_len; + + memset(strm, 0, sizeof(*strm)); + + strm->next_in = (uint8_t *)buf; + strm->avail_in = buf_size; + strm->next_out = out_buf; + strm->avail_out = out_buf_size; + + ret = inflateInit2(strm, -12); + if (ret != Z_OK) + return -1; + ret = inflate(strm, Z_FINISH); + out_len = strm->next_out - out_buf; + if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || + out_len != out_buf_size) { + inflateEnd(strm); + return -1; + } + inflateEnd(strm); + return 0; +} + +static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset) +{ + int ret, csize; + uint64_t coffset; + + coffset = cluster_offset & s->cluster_offset_mask; + if (s->cluster_cache_offset != coffset) { + csize = cluster_offset >> (63 - s->cluster_bits); + csize &= (s->cluster_size - 1); + lseek(s->fd, coffset, SEEK_SET); + ret = read(s->fd, s->cluster_data, csize); + if (ret != csize) + return -1; + if (decompress_buffer(s->cluster_cache, s->cluster_size, + s->cluster_data, csize) < 0) { + return -1; + } + s->cluster_cache_offset = coffset; + } + return 0; +} + +static int qcow_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVQcowState *s = bs->opaque; + int ret, index_in_cluster, n; + uint64_t cluster_offset; + + while (nb_sectors > 0) { + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0); + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + if (!cluster_offset) { + memset(buf, 0, 512 * n); + } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { + if (decompress_cluster(s, cluster_offset) < 0) + return -1; + memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); + } else { + lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET); + ret = read(s->fd, buf, n * 512); + if (ret != n * 512) + return -1; + if (s->crypt_method) { + encrypt_sectors(s, sector_num, buf, buf, n, 0, + &s->aes_decrypt_key); + } + } + nb_sectors -= n; + sector_num += n; + buf += n * 512; + } + return 0; +} + +static int qcow_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BDRVQcowState *s = bs->opaque; + int ret, index_in_cluster, n; + uint64_t cluster_offset; + + while (nb_sectors > 0) { + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0, + index_in_cluster, + index_in_cluster + n); + if (!cluster_offset) + return -1; + lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET); + if (s->crypt_method) { + encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1, + &s->aes_encrypt_key); + ret = write(s->fd, s->cluster_data, n * 512); + } else { + ret = write(s->fd, buf, n * 512); + } + if (ret != n * 512) + return -1; + nb_sectors -= n; + sector_num += n; + buf += n * 512; + } + s->cluster_cache_offset = -1; /* disable compressed cache */ + return 0; +} + +static void qcow_close(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + qemu_free(s->l1_table); + qemu_free(s->l2_cache); + qemu_free(s->cluster_cache); + qemu_free(s->cluster_data); + close(s->fd); +} + +static int qcow_create(const char *filename, int64_t total_size, + const char *backing_file, int flags) +{ + int fd, header_size, backing_filename_len, l1_size, i, shift; + QCowHeader header; + char backing_filename[1024]; + uint64_t tmp; + struct stat st; + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, + 0644); + if (fd < 0) + return -1; + memset(&header, 0, sizeof(header)); + header.magic = cpu_to_be32(QCOW_MAGIC); + header.version = cpu_to_be32(QCOW_VERSION); + header.size = cpu_to_be64(total_size * 512); + header_size = sizeof(header); + backing_filename_len = 0; + if (backing_file) { + if (strcmp(backing_file, "fat:")) { + const char *p; + /* XXX: this is a hack: we do not attempt to check for URL + like syntax */ + p = strchr(backing_file, ':'); + if (p && (p - backing_file) >= 2) { + /* URL like but exclude "c:" like filenames */ + pstrcpy(backing_filename, sizeof(backing_filename), + backing_file); + } else { + realpath(backing_file, backing_filename); + if (stat(backing_filename, &st) != 0) { + return -1; + } + } + header.backing_file_offset = cpu_to_be64(header_size); + backing_filename_len = strlen(backing_filename); + header.backing_file_size = cpu_to_be32(backing_filename_len); + header_size += backing_filename_len; + } else + backing_file = NULL; + header.mtime = cpu_to_be32(st.st_mtime); + header.cluster_bits = 9; /* 512 byte cluster to avoid copying + unmodifyed sectors */ + header.l2_bits = 12; /* 32 KB L2 tables */ + } else { + header.cluster_bits = 12; /* 4 KB clusters */ + header.l2_bits = 9; /* 4 KB L2 tables */ + } + header_size = (header_size + 7) & ~7; + shift = header.cluster_bits + header.l2_bits; + l1_size = ((total_size * 512) + (1LL << shift) - 1) >> shift; + + header.l1_table_offset = cpu_to_be64(header_size); + if (flags) { + header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES); + } else { + header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); + } + + /* write all the data */ + write(fd, &header, sizeof(header)); + if (backing_file) { + write(fd, backing_filename, backing_filename_len); + } + lseek(fd, header_size, SEEK_SET); + tmp = 0; + for(i = 0;i < l1_size; i++) { + write(fd, &tmp, sizeof(tmp)); + } + close(fd); + return 0; +} + +int qcow_make_empty(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + uint32_t l1_length = s->l1_size * sizeof(uint64_t); + + memset(s->l1_table, 0, l1_length); + lseek(s->fd, s->l1_table_offset, SEEK_SET); + if (write(s->fd, s->l1_table, l1_length) < 0) + return -1; + ftruncate(s->fd, s->l1_table_offset + l1_length); + + memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); + memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t)); + memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t)); + + return 0; +} + +int qcow_get_cluster_size(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + if (bs->drv != &bdrv_qcow) + return -1; + return s->cluster_size; +} + +/* XXX: put compressed sectors first, then all the cluster aligned + tables to avoid losing bytes in alignment */ +int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf) +{ + BDRVQcowState *s = bs->opaque; + z_stream strm; + int ret, out_len; + uint8_t *out_buf; + uint64_t cluster_offset; + + if (bs->drv != &bdrv_qcow) + return -1; + + out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128); + if (!out_buf) + return -1; + + /* best compression, small window, no zlib header */ + memset(&strm, 0, sizeof(strm)); + ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, + Z_DEFLATED, -12, + 9, Z_DEFAULT_STRATEGY); + if (ret != 0) { + qemu_free(out_buf); + return -1; + } + + strm.avail_in = s->cluster_size; + strm.next_in = (uint8_t *)buf; + strm.avail_out = s->cluster_size; + strm.next_out = out_buf; + + ret = deflate(&strm, Z_FINISH); + if (ret != Z_STREAM_END && ret != Z_OK) { + qemu_free(out_buf); + deflateEnd(&strm); + return -1; + } + out_len = strm.next_out - out_buf; + + deflateEnd(&strm); + + if (ret != Z_STREAM_END || out_len >= s->cluster_size) { + /* could not compress: write normal cluster */ + qcow_write(bs, sector_num, buf, s->cluster_sectors); + } else { + cluster_offset = get_cluster_offset(bs, sector_num << 9, 2, + out_len, 0, 0); + cluster_offset &= s->cluster_offset_mask; + lseek(s->fd, cluster_offset, SEEK_SET); + if (write(s->fd, out_buf, out_len) != out_len) { + qemu_free(out_buf); + return -1; + } + } + + qemu_free(out_buf); + return 0; +} + +BlockDriver bdrv_qcow = { + "qcow", + sizeof(BDRVQcowState), + qcow_probe, + qcow_open, + qcow_read, + qcow_write, + qcow_close, + qcow_create, + qcow_is_allocated, + qcow_set_key, + qcow_make_empty +}; + + diff --git a/tools/ioemu/block-vmdk.c b/tools/ioemu/block-vmdk.c new file mode 100644 index 0000000000..fc87be353b --- /dev/null +++ b/tools/ioemu/block-vmdk.c @@ -0,0 +1,439 @@ +/* + * Block driver for the VMDK format + * + * Copyright (c) 2004 Fabrice Bellard + * Copyright (c) 2005 Filip Navara + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "block_int.h" + +#define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D') +#define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V') + +typedef struct { + uint32_t version; + uint32_t flags; + uint32_t disk_sectors; + uint32_t granularity; + uint32_t l1dir_offset; + uint32_t l1dir_size; + uint32_t file_sectors; + uint32_t cylinders; + uint32_t heads; + uint32_t sectors_per_track; +} VMDK3Header; + +typedef struct { + uint32_t version; + uint32_t flags; + int64_t capacity; + int64_t granularity; + int64_t desc_offset; + int64_t desc_size; + int32_t num_gtes_per_gte; + int64_t rgd_offset; + int64_t gd_offset; + int64_t grain_offset; + char filler[1]; + char check_bytes[4]; +} __attribute__((packed)) VMDK4Header; + +#define L2_CACHE_SIZE 16 + +typedef struct BDRVVmdkState { + int fd; + int64_t l1_table_offset; + int64_t l1_backup_table_offset; + uint32_t *l1_table; + uint32_t *l1_backup_table; + unsigned int l1_size; + uint32_t l1_entry_sectors; + + unsigned int l2_size; + uint32_t *l2_cache; + uint32_t l2_cache_offsets[L2_CACHE_SIZE]; + uint32_t l2_cache_counts[L2_CACHE_SIZE]; + + unsigned int cluster_sectors; +} BDRVVmdkState; + +static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + uint32_t magic; + + if (buf_size < 4) + return 0; + magic = be32_to_cpu(*(uint32_t *)buf); + if (magic == VMDK3_MAGIC || + magic == VMDK4_MAGIC) + return 100; + else + return 0; +} + +static int vmdk_open(BlockDriverState *bs, const char *filename) +{ + BDRVVmdkState *s = bs->opaque; + int fd, i; + uint32_t magic; + int l1_size; + + fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); + if (fd < 0) { + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + if (fd < 0) + return -1; + bs->read_only = 1; + } + if (read(fd, &magic, sizeof(magic)) != sizeof(magic)) + goto fail; + magic = be32_to_cpu(magic); + if (magic == VMDK3_MAGIC) { + VMDK3Header header; + if (read(fd, &header, sizeof(header)) != + sizeof(header)) + goto fail; + s->cluster_sectors = le32_to_cpu(header.granularity); + s->l2_size = 1 << 9; + s->l1_size = 1 << 6; + bs->total_sectors = le32_to_cpu(header.disk_sectors); + s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9; + s->l1_backup_table_offset = 0; + s->l1_entry_sectors = s->l2_size * s->cluster_sectors; + } else if (magic == VMDK4_MAGIC) { + VMDK4Header header; + + if (read(fd, &header, sizeof(header)) != sizeof(header)) + goto fail; + bs->total_sectors = le64_to_cpu(header.capacity); + s->cluster_sectors = le64_to_cpu(header.granularity); + s->l2_size = le32_to_cpu(header.num_gtes_per_gte); + s->l1_entry_sectors = s->l2_size * s->cluster_sectors; + if (s->l1_entry_sectors <= 0) + goto fail; + s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1) + / s->l1_entry_sectors; + s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9; + s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9; + } else { + goto fail; + } + /* read the L1 table */ + l1_size = s->l1_size * sizeof(uint32_t); + s->l1_table = qemu_malloc(l1_size); + if (!s->l1_table) + goto fail; + if (lseek(fd, s->l1_table_offset, SEEK_SET) == -1) + goto fail; + if (read(fd, s->l1_table, l1_size) != l1_size) + goto fail; + for(i = 0; i < s->l1_size; i++) { + le32_to_cpus(&s->l1_table[i]); + } + + if (s->l1_backup_table_offset) { + s->l1_backup_table = qemu_malloc(l1_size); + if (!s->l1_backup_table) + goto fail; + if (lseek(fd, s->l1_backup_table_offset, SEEK_SET) == -1) + goto fail; + if (read(fd, s->l1_backup_table, l1_size) != l1_size) + goto fail; + for(i = 0; i < s->l1_size; i++) { + le32_to_cpus(&s->l1_backup_table[i]); + } + } + + s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t)); + if (!s->l2_cache) + goto fail; + s->fd = fd; + return 0; + fail: + qemu_free(s->l1_backup_table); + qemu_free(s->l1_table); + qemu_free(s->l2_cache); + close(fd); + return -1; +} + +static uint64_t get_cluster_offset(BlockDriverState *bs, + uint64_t offset, int allocate) +{ + BDRVVmdkState *s = bs->opaque; + unsigned int l1_index, l2_offset, l2_index; + int min_index, i, j; + uint32_t min_count, *l2_table, tmp; + uint64_t cluster_offset; + + l1_index = (offset >> 9) / s->l1_entry_sectors; + if (l1_index >= s->l1_size) + return 0; + l2_offset = s->l1_table[l1_index]; + if (!l2_offset) + return 0; + for(i = 0; i < L2_CACHE_SIZE; i++) { + if (l2_offset == s->l2_cache_offsets[i]) { + /* increment the hit count */ + if (++s->l2_cache_counts[i] == 0xffffffff) { + for(j = 0; j < L2_CACHE_SIZE; j++) { + s->l2_cache_counts[j] >>= 1; + } + } + l2_table = s->l2_cache + (i * s->l2_size); + goto found; + } + } + /* not found: load a new entry in the least used one */ + min_index = 0; + min_count = 0xffffffff; + for(i = 0; i < L2_CACHE_SIZE; i++) { + if (s->l2_cache_counts[i] < min_count) { + min_count = s->l2_cache_counts[i]; + min_index = i; + } + } + l2_table = s->l2_cache + (min_index * s->l2_size); + lseek(s->fd, (int64_t)l2_offset * 512, SEEK_SET); + if (read(s->fd, l2_table, s->l2_size * sizeof(uint32_t)) != + s->l2_size * sizeof(uint32_t)) + return 0; + s->l2_cache_offsets[min_index] = l2_offset; + s->l2_cache_counts[min_index] = 1; + found: + l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size; + cluster_offset = le32_to_cpu(l2_table[l2_index]); + if (!cluster_offset) { + if (!allocate) + return 0; + cluster_offset = lseek(s->fd, 0, SEEK_END); + ftruncate(s->fd, cluster_offset + (s->cluster_sectors << 9)); + cluster_offset >>= 9; + /* update L2 table */ + tmp = cpu_to_le32(cluster_offset); + l2_table[l2_index] = tmp; + lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET); + if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + return 0; + /* update backup L2 table */ + if (s->l1_backup_table_offset != 0) { + l2_offset = s->l1_backup_table[l1_index]; + lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET); + if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + return 0; + } + } + cluster_offset <<= 9; + return cluster_offset; +} + +static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) +{ + BDRVVmdkState *s = bs->opaque; + int index_in_cluster, n; + uint64_t cluster_offset; + + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0); + index_in_cluster = sector_num % s->cluster_sectors; + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + *pnum = n; + return (cluster_offset != 0); +} + +static int vmdk_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVVmdkState *s = bs->opaque; + int ret, index_in_cluster, n; + uint64_t cluster_offset; + + while (nb_sectors > 0) { + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0); + index_in_cluster = sector_num % s->cluster_sectors; + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + if (!cluster_offset) { + memset(buf, 0, 512 * n); + } else { + lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET); + ret = read(s->fd, buf, n * 512); + if (ret != n * 512) + return -1; + } + nb_sectors -= n; + sector_num += n; + buf += n * 512; + } + return 0; +} + +static int vmdk_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BDRVVmdkState *s = bs->opaque; + int ret, index_in_cluster, n; + uint64_t cluster_offset; + + while (nb_sectors > 0) { + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + cluster_offset = get_cluster_offset(bs, sector_num << 9, 1); + if (!cluster_offset) + return -1; + lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET); + ret = write(s->fd, buf, n * 512); + if (ret != n * 512) + return -1; + nb_sectors -= n; + sector_num += n; + buf += n * 512; + } + return 0; +} + +static int vmdk_create(const char *filename, int64_t total_size, + const char *backing_file, int flags) +{ + int fd, i; + VMDK4Header header; + uint32_t tmp, magic, grains, gd_size, gt_size, gt_count; + char *desc_template = + "# Disk DescriptorFile\n" + "version=1\n" + "CID=%x\n" + "parentCID=ffffffff\n" + "createType=\"monolithicSparse\"\n" + "\n" + "# Extent description\n" + "RW %lu SPARSE \"%s\"\n" + "\n" + "# The Disk Data Base \n" + "#DDB\n" + "\n" + "ddb.virtualHWVersion = \"3\"\n" + "ddb.geometry.cylinders = \"%lu\"\n" + "ddb.geometry.heads = \"16\"\n" + "ddb.geometry.sectors = \"63\"\n" + "ddb.adapterType = \"ide\"\n"; + char desc[1024]; + const char *real_filename, *temp_str; + + /* XXX: add support for backing file */ + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, + 0644); + if (fd < 0) + return -1; + magic = cpu_to_be32(VMDK4_MAGIC); + memset(&header, 0, sizeof(header)); + header.version = cpu_to_le32(1); + header.flags = cpu_to_le32(3); /* ?? */ + header.capacity = cpu_to_le64(total_size); + header.granularity = cpu_to_le64(128); + header.num_gtes_per_gte = cpu_to_le32(512); + + grains = (total_size + header.granularity - 1) / header.granularity; + gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9; + gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte; + gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9; + + header.desc_offset = 1; + header.desc_size = 20; + header.rgd_offset = header.desc_offset + header.desc_size; + header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count); + header.grain_offset = + ((header.gd_offset + gd_size + (gt_size * gt_count) + + header.granularity - 1) / header.granularity) * + header.granularity; + + header.desc_offset = cpu_to_le64(header.desc_offset); + header.desc_size = cpu_to_le64(header.desc_size); + header.rgd_offset = cpu_to_le64(header.rgd_offset); + header.gd_offset = cpu_to_le64(header.gd_offset); + header.grain_offset = cpu_to_le64(header.grain_offset); + + header.check_bytes[0] = 0xa; + header.check_bytes[1] = 0x20; + header.check_bytes[2] = 0xd; + header.check_bytes[3] = 0xa; + + /* write all the data */ + write(fd, &magic, sizeof(magic)); + write(fd, &header, sizeof(header)); + + ftruncate(fd, header.grain_offset << 9); + + /* write grain directory */ + lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET); + for (i = 0, tmp = header.rgd_offset + gd_size; + i < gt_count; i++, tmp += gt_size) + write(fd, &tmp, sizeof(tmp)); + + /* write backup grain directory */ + lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET); + for (i = 0, tmp = header.gd_offset + gd_size; + i < gt_count; i++, tmp += gt_size) + write(fd, &tmp, sizeof(tmp)); + + /* compose the descriptor */ + real_filename = filename; + if ((temp_str = strrchr(real_filename, '\\')) != NULL) + real_filename = temp_str + 1; + if ((temp_str = strrchr(real_filename, '/')) != NULL) + real_filename = temp_str + 1; + if ((temp_str = strrchr(real_filename, ':')) != NULL) + real_filename = temp_str + 1; + sprintf(desc, desc_template, time(NULL), (unsigned long)total_size, + real_filename, total_size / (63 * 16)); + + /* write the descriptor */ + lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET); + write(fd, desc, strlen(desc)); + + close(fd); + return 0; +} + +static void vmdk_close(BlockDriverState *bs) +{ + BDRVVmdkState *s = bs->opaque; + qemu_free(s->l1_table); + qemu_free(s->l2_cache); + close(s->fd); +} + +BlockDriver bdrv_vmdk = { + "vmdk", + sizeof(BDRVVmdkState), + vmdk_probe, + vmdk_open, + vmdk_read, + vmdk_write, + vmdk_close, + vmdk_create, + vmdk_is_allocated, +}; diff --git a/tools/ioemu/block-vpc.c b/tools/ioemu/block-vpc.c new file mode 100644 index 0000000000..e4c51bab2a --- /dev/null +++ b/tools/ioemu/block-vpc.c @@ -0,0 +1,242 @@ +/* + * Block driver for Conectix/Microsoft Virtual PC images + * + * Copyright (c) 2005 Alex Beregszaszi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "block_int.h" + +/**************************************************************/ + +#define HEADER_SIZE 512 + +//#define CACHE + +// always big-endian +struct vpc_subheader { + char magic[8]; // "conectix" / "cxsparse" + union { + struct { + uint32_t unk1[2]; + uint32_t unk2; // always zero? + uint32_t subheader_offset; + uint32_t unk3; // some size? + char creator[4]; // "vpc " + uint16_t major; + uint16_t minor; + char guest[4]; // "Wi2k" + uint32_t unk4[7]; + uint8_t vnet_id[16]; // virtual network id, purpose unknown + // next 16 longs are used, but dunno the purpose + // next 6 longs unknown, following 7 long maybe a serial + char padding[HEADER_SIZE - 84]; + } main; + struct { + uint32_t unk1[2]; // all bits set + uint32_t unk2; // always zero? + uint32_t pagetable_offset; + uint32_t unk3; + uint32_t pagetable_entries; // 32bit/entry + uint32_t pageentry_size; // 512*8*512 + uint32_t nb_sectors; + char padding[HEADER_SIZE - 40]; + } sparse; + char padding[HEADER_SIZE - 8]; + } type; +}; + +typedef struct BDRVVPCState { + int fd; + + int pagetable_entries; + uint32_t *pagetable; + + uint32_t pageentry_size; +#ifdef CACHE + uint8_t *pageentry_u8; + uint32_t *pageentry_u32; + uint16_t *pageentry_u16; + + uint64_t last_bitmap; +#endif +} BDRVVPCState; + +static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + if (buf_size >= 8 && !strncmp(buf, "conectix", 8)) + return 100; + return 0; +} + +static int vpc_open(BlockDriverState *bs, const char *filename) +{ + BDRVVPCState *s = bs->opaque; + int fd, i; + struct vpc_subheader header; + + fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); + if (fd < 0) { + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + if (fd < 0) + return -1; + } + + bs->read_only = 1; // no write support yet + + s->fd = fd; + + if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE) + goto fail; + + if (strncmp(header.magic, "conectix", 8)) + goto fail; + lseek(s->fd, be32_to_cpu(header.type.main.subheader_offset), SEEK_SET); + + if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE) + goto fail; + + if (strncmp(header.magic, "cxsparse", 8)) + goto fail; + + bs->total_sectors = ((uint64_t)be32_to_cpu(header.type.sparse.pagetable_entries) * + be32_to_cpu(header.type.sparse.pageentry_size)) / 512; + + lseek(s->fd, be32_to_cpu(header.type.sparse.pagetable_offset), SEEK_SET); + + s->pagetable_entries = be32_to_cpu(header.type.sparse.pagetable_entries); + s->pagetable = qemu_malloc(s->pagetable_entries * 4); + if (!s->pagetable) + goto fail; + if (read(s->fd, s->pagetable, s->pagetable_entries * 4) != + s->pagetable_entries * 4) + goto fail; + for (i = 0; i < s->pagetable_entries; i++) + be32_to_cpus(&s->pagetable[i]); + + s->pageentry_size = be32_to_cpu(header.type.sparse.pageentry_size); +#ifdef CACHE + s->pageentry_u8 = qemu_malloc(512); + if (!s->pageentry_u8) + goto fail; + s->pageentry_u32 = s->pageentry_u8; + s->pageentry_u16 = s->pageentry_u8; + s->last_pagetable = -1; +#endif + + return 0; + fail: + close(fd); + return -1; +} + +static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num) +{ + BDRVVPCState *s = bs->opaque; + uint64_t offset = sector_num * 512; + uint64_t bitmap_offset, block_offset; + uint32_t pagetable_index, pageentry_index; + + pagetable_index = offset / s->pageentry_size; + pageentry_index = (offset % s->pageentry_size) / 512; + + if (pagetable_index > s->pagetable_entries || s->pagetable[pagetable_index] == 0xffffffff) + return -1; // not allocated + + bitmap_offset = 512 * s->pagetable[pagetable_index]; + block_offset = bitmap_offset + 512 + (512 * pageentry_index); + +// printf("sector: %llx, index: %x, offset: %x, bioff: %llx, bloff: %llx\n", +// sector_num, pagetable_index, pageentry_index, +// bitmap_offset, block_offset); + +// disabled by reason +#if 0 +#ifdef CACHE + if (bitmap_offset != s->last_bitmap) + { + lseek(s->fd, bitmap_offset, SEEK_SET); + + s->last_bitmap = bitmap_offset; + + // Scary! Bitmap is stored as big endian 32bit entries, + // while we used to look it up byte by byte + read(s->fd, s->pageentry_u8, 512); + for (i = 0; i < 128; i++) + be32_to_cpus(&s->pageentry_u32[i]); + } + + if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1) + return -1; +#else + lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET); + + read(s->fd, &bitmap_entry, 1); + + if ((bitmap_entry >> (pageentry_index % 8)) & 1) + return -1; // not allocated +#endif +#endif + lseek(s->fd, block_offset, SEEK_SET); + + return 0; +} + +static int vpc_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVVPCState *s = bs->opaque; + int ret; + + while (nb_sectors > 0) { + if (!seek_to_sector(bs, sector_num)) + { + ret = read(s->fd, buf, 512); + if (ret != 512) + return -1; + } + else + memset(buf, 0, 512); + nb_sectors--; + sector_num++; + buf += 512; + } + return 0; +} + +static void vpc_close(BlockDriverState *bs) +{ + BDRVVPCState *s = bs->opaque; + qemu_free(s->pagetable); +#ifdef CACHE + qemu_free(s->pageentry_u8); +#endif + close(s->fd); +} + +BlockDriver bdrv_vpc = { + "vpc", + sizeof(BDRVVPCState), + vpc_probe, + vpc_open, + vpc_read, + NULL, + vpc_close, +}; diff --git a/tools/ioemu/block-vvfat.c b/tools/ioemu/block-vvfat.c new file mode 100644 index 0000000000..84d2a08ad7 --- /dev/null +++ b/tools/ioemu/block-vvfat.c @@ -0,0 +1,2807 @@ +/* vim:set shiftwidth=4 ts=8: */ +/* + * QEMU Block driver for virtual VFAT (shadows a local directory) + * + * Copyright (c) 2004,2005 Johannes E. Schindelin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include "vl.h" +#include "block_int.h" + +#ifndef S_IWGRP +#define S_IWGRP 0 +#endif +#ifndef S_IWOTH +#define S_IWOTH 0 +#endif + +/* TODO: add ":bootsector=blabla.img:" */ +/* LATER TODO: add automatic boot sector generation from + BOOTEASY.ASM and Ranish Partition Manager + Note that DOS assumes the system files to be the first files in the + file system (test if the boot sector still relies on that fact)! */ +/* MAYBE TODO: write block-visofs.c */ +/* TODO: call try_commit() only after a timeout */ + +/* #define DEBUG */ + +#ifdef DEBUG + +#define DLOG(a) a + +#undef stderr +#define stderr STDERR +FILE* stderr = NULL; + +static void checkpoint(); + +#ifdef __MINGW32__ +void nonono(const char* file, int line, const char* msg) { + fprintf(stderr, "Nonono! %s:%d %s\n", file, line, msg); + exit(-5); +} +#undef assert +#define assert(a) if (!(a)) nonono(__FILE__, __LINE__, #a) +#endif + +#else + +#define DLOG(a) + +#endif + +/* dynamic array functions */ +typedef struct array_t { + char* pointer; + unsigned int size,next,item_size; +} array_t; + +static inline void array_init(array_t* array,unsigned int item_size) +{ + array->pointer=0; + array->size=0; + array->next=0; + array->item_size=item_size; +} + +static inline void array_free(array_t* array) +{ + if(array->pointer) + free(array->pointer); + array->size=array->next=0; +} + +/* does not automatically grow */ +static inline void* array_get(array_t* array,unsigned int index) { + assert(index >= 0); + assert(index < array->next); + return array->pointer + index * array->item_size; +} + +static inline int array_ensure_allocated(array_t* array, int index) +{ + if((index + 1) * array->item_size > array->size) { + int new_size = (index + 32) * array->item_size; + array->pointer = realloc(array->pointer, new_size); + if (!array->pointer) + return -1; + array->size = new_size; + array->next = index + 1; + } + + return 0; +} + +static inline void* array_get_next(array_t* array) { + unsigned int next = array->next; + void* result; + + if (array_ensure_allocated(array, next) < 0) + return NULL; + + array->next = next + 1; + result = array_get(array, next); + + return result; +} + +static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) { + if((array->next+count)*array->item_size>array->size) { + int increment=count*array->item_size; + array->pointer=realloc(array->pointer,array->size+increment); + if(!array->pointer) + return 0; + array->size+=increment; + } + memmove(array->pointer+(index+count)*array->item_size, + array->pointer+index*array->item_size, + (array->next-index)*array->item_size); + array->next+=count; + return array->pointer+index*array->item_size; +} + +/* this performs a "roll", so that the element which was at index_from becomes + * index_to, but the order of all other elements is preserved. */ +static inline int array_roll(array_t* array,int index_to,int index_from,int count) +{ + char* buf; + char* from; + char* to; + int is; + + if(!array || + index_to<0 || index_to>=array->next || + index_from<0 || index_from>=array->next) + return -1; + + if(index_to==index_from) + return 0; + + is=array->item_size; + from=array->pointer+index_from*is; + to=array->pointer+index_to*is; + buf=malloc(is*count); + memcpy(buf,from,is*count); + + if(index_to=0); + assert(count > 0); + assert(index + count <= array->next); + if(array_roll(array,array->next-1,index,count)) + return -1; + array->next -= count; + return 0; +} + +int array_remove(array_t* array,int index) +{ + return array_remove_slice(array, index, 1); +} + +/* return the index for a given member */ +int array_index(array_t* array, void* pointer) +{ + size_t offset = (char*)pointer - array->pointer; + assert(offset >= 0); + assert((offset % array->item_size) == 0); + assert(offset/array->item_size < array->next); + return offset/array->item_size; +} + +/* These structures are used to fake a disk and the VFAT filesystem. + * For this reason we need to use __attribute__((packed)). */ + +typedef struct bootsector_t { + uint8_t jump[3]; + uint8_t name[8]; + uint16_t sector_size; + uint8_t sectors_per_cluster; + uint16_t reserved_sectors; + uint8_t number_of_fats; + uint16_t root_entries; + uint16_t total_sectors16; + uint8_t media_type; + uint16_t sectors_per_fat; + uint16_t sectors_per_track; + uint16_t number_of_heads; + uint32_t hidden_sectors; + uint32_t total_sectors; + union { + struct { + uint8_t drive_number; + uint8_t current_head; + uint8_t signature; + uint32_t id; + uint8_t volume_label[11]; + } __attribute__((packed)) fat16; + struct { + uint32_t sectors_per_fat; + uint16_t flags; + uint8_t major,minor; + uint32_t first_cluster_of_root_directory; + uint16_t info_sector; + uint16_t backup_boot_sector; + uint16_t ignored; + } __attribute__((packed)) fat32; + } u; + uint8_t fat_type[8]; + uint8_t ignored[0x1c0]; + uint8_t magic[2]; +} __attribute__((packed)) bootsector_t; + +typedef struct partition_t { + uint8_t attributes; /* 0x80 = bootable */ + uint8_t start_head; + uint8_t start_sector; + uint8_t start_cylinder; + uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xb = FAT32 */ + uint8_t end_head; + uint8_t end_sector; + uint8_t end_cylinder; + uint32_t start_sector_long; + uint32_t end_sector_long; +} __attribute__((packed)) partition_t; + +typedef struct mbr_t { + uint8_t ignored[0x1be]; + partition_t partition[4]; + uint8_t magic[2]; +} __attribute__((packed)) mbr_t; + +typedef struct direntry_t { + uint8_t name[8]; + uint8_t extension[3]; + uint8_t attributes; + uint8_t reserved[2]; + uint16_t ctime; + uint16_t cdate; + uint16_t adate; + uint16_t begin_hi; + uint16_t mtime; + uint16_t mdate; + uint16_t begin; + uint32_t size; +} __attribute__((packed)) direntry_t; + +/* this structure are used to transparently access the files */ + +typedef struct mapping_t { + /* begin is the first cluster, end is the last+1 */ + uint32_t begin,end; + /* as s->directory is growable, no pointer may be used here */ + unsigned int dir_index; + /* the clusters of a file may be in any order; this points to the first */ + int first_mapping_index; + union { + /* offset is + * - the offset in the file (in clusters) for a file, or + * - the next cluster of the directory for a directory, and + * - the address of the buffer for a faked entry + */ + struct { + uint32_t offset; + } file; + struct { + int parent_mapping_index; + int first_dir_index; + } dir; + } info; + /* path contains the full path, i.e. it always starts with s->path */ + char* path; + + enum { MODE_UNDEFINED = 0, MODE_NORMAL = 1, MODE_MODIFIED = 2, + MODE_DIRECTORY = 4, MODE_FAKED = 8, + MODE_DELETED = 16, MODE_RENAMED = 32 } mode; + int read_only; +} mapping_t; + +#ifdef DEBUG +static void print_direntry(const struct direntry_t*); +static void print_mapping(const struct mapping_t* mapping); +#endif + +/* here begins the real VVFAT driver */ + +typedef struct BDRVVVFATState { + BlockDriverState* bs; /* pointer to parent */ + unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */ + unsigned char first_sectors[0x40*0x200]; + + int fat_type; /* 16 or 32 */ + array_t fat,directory,mapping; + + unsigned int cluster_size; + unsigned int sectors_per_cluster; + unsigned int sectors_per_fat; + unsigned int sectors_of_root_directory; + uint32_t last_cluster_of_root_directory; + unsigned int faked_sectors; /* how many sectors are faked before file data */ + uint32_t sector_count; /* total number of sectors of the partition */ + uint32_t cluster_count; /* total number of clusters of this partition */ + uint32_t max_fat_value; + + int current_fd; + mapping_t* current_mapping; + unsigned char* cluster; /* points to current cluster */ + unsigned char* cluster_buffer; /* points to a buffer to hold temp data */ + unsigned int current_cluster; + + /* write support */ + BlockDriverState* write_target; + char* qcow_filename; + BlockDriverState* qcow; + void* fat2; + char* used_clusters; + array_t commits; + const char* path; + int downcase_short_names; +} BDRVVVFATState; + + +static int vvfat_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + if (strstart(filename, "fat:", NULL)) + return 100; + return 0; +} + +static void init_mbr(BDRVVVFATState* s) +{ + /* TODO: if the files mbr.img and bootsect.img exist, use them */ + mbr_t* real_mbr=(mbr_t*)s->first_sectors; + partition_t* partition=&(real_mbr->partition[0]); + + memset(s->first_sectors,0,512); + + partition->attributes=0x80; /* bootable */ + partition->start_head=1; + partition->start_sector=1; + partition->start_cylinder=0; + /* FAT12/FAT16/FAT32 */ + partition->fs_type=(s->fat_type==12?0x1:s->fat_type==16?0x6:0xb); + partition->end_head=s->bs->heads-1; + partition->end_sector=0xff; /* end sector & upper 2 bits of cylinder */; + partition->end_cylinder=0xff; /* lower 8 bits of end cylinder */; + partition->start_sector_long=cpu_to_le32(s->bs->secs); + partition->end_sector_long=cpu_to_le32(s->sector_count); + + real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa; +} + +/* direntry functions */ + +/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */ +static inline int short2long_name(unsigned char* dest,const char* src) +{ + int i; + for(i=0;i<129 && src[i];i++) { + dest[2*i]=src[i]; + dest[2*i+1]=0; + } + dest[2*i]=dest[2*i+1]=0; + for(i=2*i+2;(i%26);i++) + dest[i]=0xff; + return i; +} + +static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename) +{ + char buffer[258]; + int length=short2long_name(buffer,filename), + number_of_entries=(length+25)/26,i; + direntry_t* entry; + + for(i=0;idirectory)); + entry->attributes=0xf; + entry->reserved[0]=0; + entry->begin=0; + entry->name[0]=(number_of_entries-i)|(i==0?0x40:0); + } + for(i=0;idirectory),s->directory.next-1-(i/26)); + entry->name[offset]=buffer[i]; + } + return array_get(&(s->directory),s->directory.next-number_of_entries); +} + +static char is_free(const direntry_t* direntry) +{ + /* return direntry->name[0]==0 ; */ + return direntry->attributes == 0 || direntry->name[0]==0xe5; +} + +static char is_volume_label(const direntry_t* direntry) +{ + return direntry->attributes == 0x28; +} + +static char is_long_name(const direntry_t* direntry) +{ + return direntry->attributes == 0xf; +} + +static char is_short_name(const direntry_t* direntry) +{ + return !is_volume_label(direntry) && !is_long_name(direntry) + && !is_free(direntry); +} + +static char is_directory(const direntry_t* direntry) +{ + return direntry->attributes & 0x10 && direntry->name[0] != 0xe5; +} + +static inline char is_dot(const direntry_t* direntry) +{ + return is_short_name(direntry) && direntry->name[0] == '.'; +} + +static char is_file(const direntry_t* direntry) +{ + return is_short_name(direntry) && !is_directory(direntry); +} + +static inline uint32_t begin_of_direntry(const direntry_t* direntry) +{ + return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16); +} + +static inline uint32_t filesize_of_direntry(const direntry_t* direntry) +{ + return le32_to_cpu(direntry->size); +} + +static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin) +{ + direntry->begin = cpu_to_le16(begin & 0xffff); + direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff); +} + +/* fat functions */ + +static inline uint8_t fat_chksum(const direntry_t* entry) +{ + uint8_t chksum=0; + int i; + + for(i=0;i<11;i++) + chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + +(unsigned char)entry->name[i]; + + return chksum; +} + +/* if return_time==0, this returns the fat_date, else the fat_time */ +static uint16_t fat_datetime(time_t time,int return_time) { + struct tm* t; +#ifdef _WIN32 + t=localtime(&time); /* this is not thread safe */ +#else + struct tm t1; + t=&t1; + localtime_r(&time,t); +#endif + if(return_time) + return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11)); + return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9)); +} + +static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value) +{ + if(s->fat_type==32) { + uint32_t* entry=array_get(&(s->fat),cluster); + *entry=cpu_to_le32(value); + } else if(s->fat_type==16) { + uint16_t* entry=array_get(&(s->fat),cluster); + *entry=cpu_to_le16(value&0xffff); + } else { + int offset = (cluster*3/2); + unsigned char* p = array_get(&(s->fat), offset); + switch (cluster&1) { + case 0: + p[0] = value&0xff; + p[1] = (p[1]&0xf0) | ((value>>8)&0xf); + break; + case 1: + p[0] = (p[0]&0xf) | ((value&0xf)<<4); + p[1] = (value>>4); + break; + } + } +} + +static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster) +{ + if(s->fat_type==32) { + uint32_t* entry=array_get(&(s->fat),cluster); + return le32_to_cpu(*entry); + } else if(s->fat_type==16) { + uint16_t* entry=array_get(&(s->fat),cluster); + return le16_to_cpu(*entry); + } else { + const uint8_t* x=s->fat.pointer+cluster*3/2; + return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff; + } +} + +static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry) +{ + if(fat_entry>s->max_fat_value-8) + return -1; + return 0; +} + +static inline void init_fat(BDRVVVFATState* s) +{ + if (s->fat_type == 12) { + array_init(&(s->fat),1); + array_ensure_allocated(&(s->fat), + s->sectors_per_fat * 0x200 * 3 / 2 - 1); + } else { + array_init(&(s->fat),(s->fat_type==32?4:2)); + array_ensure_allocated(&(s->fat), + s->sectors_per_fat * 0x200 / s->fat.item_size - 1); + } + memset(s->fat.pointer,0,s->fat.size); + + switch(s->fat_type) { + case 12: s->max_fat_value=0xfff; break; + case 16: s->max_fat_value=0xffff; break; + case 32: s->max_fat_value=0x0fffffff; break; + default: s->max_fat_value=0; /* error... */ + } + +} + +/* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */ +/* TODO: in parse_short_filename, 0x05->0xe5 is not yet handled! */ +static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s, + unsigned int directory_start, const char* filename, int is_dot) +{ + int i,j,long_index=s->directory.next; + direntry_t* entry=0; + direntry_t* entry_long=0; + + if(is_dot) { + entry=array_get_next(&(s->directory)); + memset(entry->name,0x20,11); + memcpy(entry->name,filename,strlen(filename)); + return entry; + } + + entry_long=create_long_filename(s,filename); + + i = strlen(filename); + for(j = i - 1; j>0 && filename[j]!='.';j--); + if (j > 0) + i = (j > 8 ? 8 : j); + else if (i > 8) + i = 8; + + entry=array_get_next(&(s->directory)); + memset(entry->name,0x20,11); + strncpy(entry->name,filename,i); + + if(j > 0) + for (i = 0; i < 3 && filename[j+1+i]; i++) + entry->extension[i] = filename[j+1+i]; + + /* upcase & remove unwanted characters */ + for(i=10;i>=0;i--) { + if(i==10 || i==7) for(;i>0 && entry->name[i]==' ';i--); + if(entry->name[i]<=' ' || entry->name[i]>0x7f + || strchr(".*?<>|\":/\\[];,+='",entry->name[i])) + entry->name[i]='_'; + else if(entry->name[i]>='a' && entry->name[i]<='z') + entry->name[i]+='A'-'a'; + } + + /* mangle duplicates */ + while(1) { + direntry_t* entry1=array_get(&(s->directory),directory_start); + int j; + + for(;entry1name,entry->name,11)) + break; /* found dupe */ + if(entry1==entry) /* no dupe found */ + break; + + /* use all 8 characters of name */ + if(entry->name[7]==' ') { + int j; + for(j=6;j>0 && entry->name[j]==' ';j--) + entry->name[j]='~'; + } + + /* increment number */ + for(j=7;j>0 && entry->name[j]=='9';j--) + entry->name[j]='0'; + if(j>0) { + if(entry->name[j]<'0' || entry->name[j]>'9') + entry->name[j]='0'; + else + entry->name[j]++; + } + } + + /* calculate checksum; propagate to long name */ + if(entry_long) { + uint8_t chksum=fat_chksum(entry); + + /* calculate anew, because realloc could have taken place */ + entry_long=array_get(&(s->directory),long_index); + while(entry_longreserved[1]=chksum; + entry_long++; + } + } + + return entry; +} + +/* + * Read a directory. (the index of the corresponding mapping must be passed). + */ +static int read_directory(BDRVVVFATState* s, int mapping_index) +{ + mapping_t* mapping = array_get(&(s->mapping), mapping_index); + direntry_t* direntry; + const char* dirname = mapping->path; + int first_cluster = mapping->begin; + int parent_index = mapping->info.dir.parent_mapping_index; + mapping_t* parent_mapping = (mapping_t*) + (parent_index >= 0 ? array_get(&(s->mapping), parent_index) : 0); + int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1; + + DIR* dir=opendir(dirname); + struct dirent* entry; + int i; + + assert(mapping->mode & MODE_DIRECTORY); + + if(!dir) { + mapping->end = mapping->begin; + return -1; + } + + i = mapping->info.dir.first_dir_index = + first_cluster == 0 ? 0 : s->directory.next; + + /* actually read the directory, and allocate the mappings */ + while((entry=readdir(dir))) { + unsigned int length=strlen(dirname)+2+strlen(entry->d_name); + char* buffer; + direntry_t* direntry; + struct stat st; + int is_dot=!strcmp(entry->d_name,"."); + int is_dotdot=!strcmp(entry->d_name,".."); + + if(first_cluster == 0 && (is_dotdot || is_dot)) + continue; + + buffer=(char*)malloc(length); + assert(buffer); + snprintf(buffer,length,"%s/%s",dirname,entry->d_name); + + if(stat(buffer,&st)<0) { + free(buffer); + continue; + } + + /* create directory entry for this file */ + direntry=create_short_and_long_name(s, i, entry->d_name, + is_dot || is_dotdot); + direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20); + direntry->reserved[0]=direntry->reserved[1]=0; + direntry->ctime=fat_datetime(st.st_ctime,1); + direntry->cdate=fat_datetime(st.st_ctime,0); + direntry->adate=fat_datetime(st.st_atime,0); + direntry->begin_hi=0; + direntry->mtime=fat_datetime(st.st_mtime,1); + direntry->mdate=fat_datetime(st.st_mtime,0); + if(is_dotdot) + set_begin_of_direntry(direntry, first_cluster_of_parent); + else if(is_dot) + set_begin_of_direntry(direntry, first_cluster); + else + direntry->begin=0; /* do that later */ + if (st.st_size > 0x7fffffff) { + fprintf(stderr, "File %s is larger than 2GB\n", buffer); + free(buffer); + return -2; + } + direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size); + + /* create mapping for this file */ + if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) { + s->current_mapping=(mapping_t*)array_get_next(&(s->mapping)); + s->current_mapping->begin=0; + s->current_mapping->end=st.st_size; + /* + * we get the direntry of the most recent direntry, which + * contains the short name and all the relevant information. + */ + s->current_mapping->dir_index=s->directory.next-1; + s->current_mapping->first_mapping_index = -1; + if (S_ISDIR(st.st_mode)) { + s->current_mapping->mode = MODE_DIRECTORY; + s->current_mapping->info.dir.parent_mapping_index = + mapping_index; + } else { + s->current_mapping->mode = MODE_UNDEFINED; + s->current_mapping->info.file.offset = 0; + } + s->current_mapping->path=buffer; + s->current_mapping->read_only = + (st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0; + } + } + closedir(dir); + + /* fill with zeroes up to the end of the cluster */ + while(s->directory.next%(0x10*s->sectors_per_cluster)) { + direntry_t* direntry=array_get_next(&(s->directory)); + memset(direntry,0,sizeof(direntry_t)); + } + +/* TODO: if there are more entries, bootsector has to be adjusted! */ +#define ROOT_ENTRIES (0x02 * 0x10 * s->sectors_per_cluster) + if (mapping_index == 0 && s->directory.next < ROOT_ENTRIES) { + /* root directory */ + int cur = s->directory.next; + array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1); + memset(array_get(&(s->directory), cur), 0, + (ROOT_ENTRIES - cur) * sizeof(direntry_t)); + } + + /* reget the mapping, since s->mapping was possibly realloc()ed */ + mapping = (mapping_t*)array_get(&(s->mapping), mapping_index); + first_cluster += (s->directory.next - mapping->info.dir.first_dir_index) + * 0x20 / s->cluster_size; + mapping->end = first_cluster; + + direntry = (direntry_t*)array_get(&(s->directory), mapping->dir_index); + set_begin_of_direntry(direntry, mapping->begin); + + return 0; +} + +static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num) +{ + return (sector_num-s->faked_sectors)/s->sectors_per_cluster; +} + +static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num) +{ + return s->faked_sectors + s->sectors_per_cluster * cluster_num; +} + +static inline uint32_t sector_offset_in_cluster(BDRVVVFATState* s,off_t sector_num) +{ + return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)%s->sectors_per_cluster; +} + +#ifdef DBG +static direntry_t* get_direntry_for_mapping(BDRVVVFATState* s,mapping_t* mapping) +{ + if(mapping->mode==MODE_UNDEFINED) + return 0; + return (direntry_t*)(s->directory.pointer+sizeof(direntry_t)*mapping->dir_index); +} +#endif + +static int init_directories(BDRVVVFATState* s, + const char* dirname) +{ + bootsector_t* bootsector; + mapping_t* mapping; + unsigned int i; + unsigned int cluster; + + memset(&(s->first_sectors[0]),0,0x40*0x200); + + s->cluster_size=s->sectors_per_cluster*0x200; + s->cluster_buffer=malloc(s->cluster_size); + assert(s->cluster_buffer); + + /* + * The formula: sc = spf+1+spf*spc*(512*8/fat_type), + * where sc is sector_count, + * spf is sectors_per_fat, + * spc is sectors_per_clusters, and + * fat_type = 12, 16 or 32. + */ + i = 1+s->sectors_per_cluster*0x200*8/s->fat_type; + s->sectors_per_fat=(s->sector_count+i)/i; /* round up */ + + array_init(&(s->mapping),sizeof(mapping_t)); + array_init(&(s->directory),sizeof(direntry_t)); + + /* add volume label */ + { + direntry_t* entry=array_get_next(&(s->directory)); + entry->attributes=0x28; /* archive | volume label */ + snprintf(entry->name,11,"QEMU VVFAT"); + } + + /* Now build FAT, and write back information into directory */ + init_fat(s); + + s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2; + s->cluster_count=sector2cluster(s, s->sector_count); + + mapping = array_get_next(&(s->mapping)); + mapping->begin = 0; + mapping->dir_index = 0; + mapping->info.dir.parent_mapping_index = -1; + mapping->first_mapping_index = -1; + mapping->path = strdup(dirname); + i = strlen(mapping->path); + if (i > 0 && mapping->path[i - 1] == '/') + mapping->path[i - 1] = '\0'; + mapping->mode = MODE_DIRECTORY; + mapping->read_only = 0; + s->path = mapping->path; + + for (i = 0, cluster = 0; i < s->mapping.next; i++) { + int j; + /* MS-DOS expects the FAT to be 0 for the root directory + * (except for the media byte). */ + /* LATER TODO: still true for FAT32? */ + int fix_fat = (i != 0); + mapping = array_get(&(s->mapping), i); + + if (mapping->mode & MODE_DIRECTORY) { + mapping->begin = cluster; + if(read_directory(s, i)) { + fprintf(stderr, "Could not read directory %s\n", + mapping->path); + return -1; + } + mapping = array_get(&(s->mapping), i); + } else { + assert(mapping->mode == MODE_UNDEFINED); + mapping->mode=MODE_NORMAL; + mapping->begin = cluster; + if (mapping->end > 0) { + direntry_t* direntry = array_get(&(s->directory), + mapping->dir_index); + + mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size; + set_begin_of_direntry(direntry, mapping->begin); + } else { + mapping->end = cluster + 1; + fix_fat = 0; + } + } + + assert(mapping->begin < mapping->end); + + /* fix fat for entry */ + if (fix_fat) { + for(j = mapping->begin; j < mapping->end - 1; j++) + fat_set(s, j, j+1); + fat_set(s, mapping->end - 1, s->max_fat_value); + } + + /* next free cluster */ + cluster = mapping->end; + + if(cluster > s->cluster_count) { + fprintf(stderr,"Directory does not fit in FAT%d\n",s->fat_type); + return -1; + } + } + + mapping = array_get(&(s->mapping), 0); + s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster; + s->last_cluster_of_root_directory = mapping->end; + + /* the FAT signature */ + fat_set(s,0,s->max_fat_value); + fat_set(s,1,s->max_fat_value); + + s->current_mapping = NULL; + + bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200); + bootsector->jump[0]=0xeb; + bootsector->jump[1]=0x3e; + bootsector->jump[2]=0x90; + memcpy(bootsector->name,"QEMU ",8); + bootsector->sector_size=cpu_to_le16(0x200); + bootsector->sectors_per_cluster=s->sectors_per_cluster; + bootsector->reserved_sectors=cpu_to_le16(1); + bootsector->number_of_fats=0x2; /* number of FATs */ + bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10); + bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count); + bootsector->media_type=(s->fat_type!=12?0xf8:s->sector_count==5760?0xf9:0xf8); /* media descriptor */ + s->fat.pointer[0] = bootsector->media_type; + bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat); + bootsector->sectors_per_track=cpu_to_le16(s->bs->secs); + bootsector->number_of_heads=cpu_to_le16(s->bs->heads); + bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f); + bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0); + + /* LATER TODO: if FAT32, this is wrong */ + bootsector->u.fat16.drive_number=s->fat_type==12?0:0x80; /* assume this is hda (TODO) */ + bootsector->u.fat16.current_head=0; + bootsector->u.fat16.signature=0x29; + bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd); + + memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11); + memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8); + bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa; + + return 0; +} + +static BDRVVVFATState *vvv = NULL; + +static int enable_write_target(BDRVVVFATState *s); +static int is_consistent(BDRVVVFATState *s); + +static int vvfat_open(BlockDriverState *bs, const char* dirname) +{ + BDRVVVFATState *s = bs->opaque; + int floppy = 0; + int i; + + vvv = s; + +DLOG(if (stderr == NULL) { + stderr = fopen("vvfat.log", "a"); + setbuf(stderr, NULL); +}) + + s->bs = bs; + + s->fat_type=16; + /* LATER TODO: if FAT32, adjust */ + s->sector_count=0xec04f; + s->sectors_per_cluster=0x10; + /* LATER TODO: this could be wrong for FAT32 */ + bs->cyls=1023; bs->heads=15; bs->secs=63; + + s->current_cluster=0xffffffff; + + s->first_sectors_number=0x40; + /* read only is the default for safety */ + bs->read_only = 1; + s->qcow = s->write_target = NULL; + s->qcow_filename = NULL; + s->fat2 = NULL; + s->downcase_short_names = 1; + + if (!strstart(dirname, "fat:", NULL)) + return -1; + + if (strstr(dirname, ":rw:")) { + if (enable_write_target(s)) + return -1; + bs->read_only = 0; + } + + if (strstr(dirname, ":floppy:")) { + floppy = 1; + s->fat_type = 12; + s->first_sectors_number = 1; + s->sectors_per_cluster=2; + bs->cyls = 80; bs->heads = 2; bs->secs = 36; + } + + if (strstr(dirname, ":32:")) { + fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n"); + s->fat_type = 32; + } else if (strstr(dirname, ":16:")) { + s->fat_type = 16; + } else if (strstr(dirname, ":12:")) { + s->fat_type = 12; + s->sector_count=2880; + } + + i = strrchr(dirname, ':') - dirname; + assert(i >= 3); + if (dirname[i-2] == ':' && isalpha(dirname[i-1])) + /* workaround for DOS drive names */ + dirname += i-1; + else + dirname += i+1; + + bs->total_sectors=bs->cyls*bs->heads*bs->secs; + if (s->sector_count > bs->total_sectors) + s->sector_count = bs->total_sectors; + if(init_directories(s, dirname)) + return -1; + + if(s->first_sectors_number==0x40) + init_mbr(s); + + /* for some reason or other, MS-DOS does not like to know about CHS... */ + if (floppy) + bs->heads = bs->cyls = bs->secs = 0; + + // assert(is_consistent(s)); + + return 0; +} + +static inline void vvfat_close_current_file(BDRVVVFATState *s) +{ + if(s->current_mapping) { + s->current_mapping = NULL; + if (s->current_fd) { + close(s->current_fd); + s->current_fd = 0; + } + } + s->current_cluster = -1; +} + +/* mappings between index1 and index2-1 are supposed to be ordered + * return value is the index of the last mapping for which end>cluster_num + */ +static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2) +{ + int index3=index1+1; + while(1) { + mapping_t* mapping; + index3=(index1+index2)/2; + mapping=array_get(&(s->mapping),index3); + assert(mapping->begin < mapping->end); + if(mapping->begin>=cluster_num) { + assert(index2!=index3 || index2==0); + if(index2==index3) + return index1; + index2=index3; + } else { + if(index1==index3) + return mapping->end<=cluster_num ? index2 : index1; + index1=index3; + } + assert(index1<=index2); + DLOG(mapping=array_get(&(s->mapping),index1); + assert(mapping->begin<=cluster_num); + assert(index2 >= s->mapping.next || + ((mapping = array_get(&(s->mapping),index2)) && + mapping->end>cluster_num))); + } +} + +static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num) +{ + int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next); + mapping_t* mapping; + if(index>=s->mapping.next) + return 0; + mapping=array_get(&(s->mapping),index); + if(mapping->begin>cluster_num) + return 0; + assert(mapping->begin<=cluster_num && mapping->end>cluster_num); + return mapping; +} + +/* + * This function simply compares path == mapping->path. Since the mappings + * are sorted by cluster, this is expensive: O(n). + */ +static inline mapping_t* find_mapping_for_path(BDRVVVFATState* s, + const char* path) +{ + int i; + + for (i = 0; i < s->mapping.next; i++) { + mapping_t* mapping = array_get(&(s->mapping), i); + if (mapping->first_mapping_index < 0 && + !strcmp(path, mapping->path)) + return mapping; + } + + return NULL; +} + +static int open_file(BDRVVVFATState* s,mapping_t* mapping) +{ + if(!mapping) + return -1; + if(!s->current_mapping || + strcmp(s->current_mapping->path,mapping->path)) { + /* open file */ + int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE); + if(fd<0) + return -1; + vvfat_close_current_file(s); + s->current_fd = fd; + s->current_mapping = mapping; + } + return 0; +} + +static inline int read_cluster(BDRVVVFATState *s,int cluster_num) +{ + if(s->current_cluster != cluster_num) { + int result=0; + off_t offset; + assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY)); + if(!s->current_mapping + || s->current_mapping->begin>cluster_num + || s->current_mapping->end<=cluster_num) { + /* binary search of mappings for file */ + mapping_t* mapping=find_mapping_for_cluster(s,cluster_num); + + assert(!mapping || (cluster_num>=mapping->begin && cluster_numend)); + + if (mapping && mapping->mode & MODE_DIRECTORY) { + vvfat_close_current_file(s); + s->current_mapping = mapping; +read_cluster_directory: + offset = s->cluster_size*(cluster_num-s->current_mapping->begin); + s->cluster = s->directory.pointer+offset + + 0x20*s->current_mapping->info.dir.first_dir_index; + assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0); + assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size); + s->current_cluster = cluster_num; + return 0; + } + + if(open_file(s,mapping)) + return -2; + } else if (s->current_mapping->mode & MODE_DIRECTORY) + goto read_cluster_directory; + + assert(s->current_fd); + + offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset; + if(lseek(s->current_fd, offset, SEEK_SET)!=offset) + return -3; + s->cluster=s->cluster_buffer; + result=read(s->current_fd,s->cluster,s->cluster_size); + if(result<0) { + s->current_cluster = -1; + return -1; + } + s->current_cluster = cluster_num; + } + return 0; +} + +#ifdef DEBUG +static void hexdump(const void* address, uint32_t len) +{ + const unsigned char* p = address; + int i, j; + + for (i = 0; i < len; i += 16) { + for (j = 0; j < 16 && i + j < len; j++) + fprintf(stderr, "%02x ", p[i + j]); + for (; j < 16; j++) + fprintf(stderr, " "); + fprintf(stderr, " "); + for (j = 0; j < 16 && i + j < len; j++) + fprintf(stderr, "%c", (p[i + j] < ' ' || p[i + j] > 0x7f) ? '.' : p[i + j]); + fprintf(stderr, "\n"); + } +} + +static void print_direntry(const direntry_t* direntry) +{ + int j = 0; + char buffer[1024]; + + fprintf(stderr, "direntry 0x%x: ", (int)direntry); + if(!direntry) + return; + if(is_long_name(direntry)) { + unsigned char* c=(unsigned char*)direntry; + int i; + for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2) +#define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = '°'; j++;} + ADD_CHAR(c[i]); + for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2) + ADD_CHAR(c[i]); + for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2) + ADD_CHAR(c[i]); + buffer[j] = 0; + fprintf(stderr, "%s\n", buffer); + } else { + int i; + for(i=0;i<11;i++) + ADD_CHAR(direntry->name[i]); + buffer[j] = 0; + fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n", + buffer, + direntry->attributes, + begin_of_direntry(direntry),le32_to_cpu(direntry->size)); + } +} + +static void print_mapping(const mapping_t* mapping) +{ + fprintf(stderr, "mapping (0x%x): begin, end = %d, %d, dir_index = %d, first_mapping_index = %d, name = %s, mode = 0x%x, " , (int)mapping, mapping->begin, mapping->end, mapping->dir_index, mapping->first_mapping_index, mapping->path, mapping->mode); + if (mapping->mode & MODE_DIRECTORY) + fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index); + else + fprintf(stderr, "offset = %d\n", mapping->info.file.offset); +} +#endif + +static int vvfat_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVVVFATState *s = bs->opaque; + int i; + + for(i=0;i= s->sector_count) + return -1; + if (s->qcow) { + int n; + if (s->qcow->drv->bdrv_is_allocated(s->qcow, + sector_num, nb_sectors-i, &n)) { +DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n)); + if (s->qcow->drv->bdrv_read(s->qcow, sector_num, buf+i*0x200, n)) + return -1; + i += n - 1; + sector_num += n - 1; + continue; + } +DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num)); + } + if(sector_numfaked_sectors) { + if(sector_numfirst_sectors_number) + memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200); + else if(sector_num-s->first_sectors_numbersectors_per_fat) + memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200); + else if(sector_num-s->first_sectors_number-s->sectors_per_fatsectors_per_fat) + memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200); + } else { + uint32_t sector=sector_num-s->faked_sectors, + sector_offset_in_cluster=(sector%s->sectors_per_cluster), + cluster_num=sector/s->sectors_per_cluster; + if(read_cluster(s, cluster_num) != 0) { + /* LATER TODO: strict: return -1; */ + memset(buf+i*0x200,0,0x200); + continue; + } + memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200); + } + } + return 0; +} + +/* LATER TODO: statify all functions */ + +/* + * Idea of the write support (use snapshot): + * + * 1. check if all data is consistent, recording renames, modifications, + * new files and directories (in s->commits). + * + * 2. if the data is not consistent, stop committing + * + * 3. handle renames, and create new files and directories (do not yet + * write their contents) + * + * 4. walk the directories, fixing the mapping and direntries, and marking + * the handled mappings as not deleted + * + * 5. commit the contents of the files + * + * 6. handle deleted files and directories + * + */ + +typedef struct commit_t { + char* path; + union { + struct { uint32_t cluster; } rename; + struct { int dir_index; uint32_t modified_offset; } writeout; + struct { uint32_t first_cluster; } new_file; + struct { uint32_t cluster; } mkdir; + } param; + /* DELETEs and RMDIRs are handled differently: see handle_deletes() */ + enum { + ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR + } action; +} commit_t; + +static void clear_commits(BDRVVVFATState* s) +{ + int i; +DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next)); + for (i = 0; i < s->commits.next; i++) { + commit_t* commit = array_get(&(s->commits), i); + assert(commit->path || commit->action == ACTION_WRITEOUT); + if (commit->action != ACTION_WRITEOUT) { + assert(commit->path); + free(commit->path); + } else + assert(commit->path == NULL); + } + s->commits.next = 0; +} + +static void schedule_rename(BDRVVVFATState* s, + uint32_t cluster, char* new_path) +{ + commit_t* commit = array_get_next(&(s->commits)); + commit->path = new_path; + commit->param.rename.cluster = cluster; + commit->action = ACTION_RENAME; +} + +static void schedule_writeout(BDRVVVFATState* s, + int dir_index, uint32_t modified_offset) +{ + commit_t* commit = array_get_next(&(s->commits)); + commit->path = NULL; + commit->param.writeout.dir_index = dir_index; + commit->param.writeout.modified_offset = modified_offset; + commit->action = ACTION_WRITEOUT; +} + +static void schedule_new_file(BDRVVVFATState* s, + char* path, uint32_t first_cluster) +{ + commit_t* commit = array_get_next(&(s->commits)); + commit->path = path; + commit->param.new_file.first_cluster = first_cluster; + commit->action = ACTION_NEW_FILE; +} + +static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path) +{ + commit_t* commit = array_get_next(&(s->commits)); + commit->path = path; + commit->param.mkdir.cluster = cluster; + commit->action = ACTION_MKDIR; +} + +typedef struct { + unsigned char name[1024]; + int checksum, len; + int sequence_number; +} long_file_name; + +static void lfn_init(long_file_name* lfn) +{ + lfn->sequence_number = lfn->len = 0; + lfn->checksum = 0x100; +} + +/* return 0 if parsed successfully, > 0 if no long name, < 0 if error */ +static int parse_long_name(long_file_name* lfn, + const direntry_t* direntry) +{ + int i, j, offset; + const unsigned char* pointer = (const unsigned char*)direntry; + + if (!is_long_name(direntry)) + return 1; + + if (pointer[0] & 0x40) { + lfn->sequence_number = pointer[0] & 0x3f; + lfn->checksum = pointer[13]; + lfn->name[0] = 0; + } else if ((pointer[0] & 0x3f) != --lfn->sequence_number) + return -1; + else if (pointer[13] != lfn->checksum) + return -2; + else if (pointer[12] || pointer[26] || pointer[27]) + return -3; + + offset = 13 * (lfn->sequence_number - 1); + for (i = 0, j = 1; i < 13; i++, j+=2) { + if (j == 11) + j = 14; + else if (j == 26) + j = 28; + + if (pointer[j+1] == 0) + lfn->name[offset + i] = pointer[j]; + else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0) + return -4; + else + lfn->name[offset + i] = 0; + } + + if (pointer[0] & 0x40) + lfn->len = offset + strlen(lfn->name + offset); + + return 0; +} + +/* returns 0 if successful, >0 if no short_name, and <0 on error */ +static int parse_short_name(BDRVVVFATState* s, + long_file_name* lfn, direntry_t* direntry) +{ + int i, j; + + if (!is_short_name(direntry)) + return 1; + + for (j = 7; j >= 0 && direntry->name[j] == ' '; j--); + for (i = 0; i <= j; i++) { + if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f) + return -1; + else if (s->downcase_short_names) + lfn->name[i] = tolower(direntry->name[i]); + else + lfn->name[i] = direntry->name[i]; + } + + for (j = 2; j >= 0 && direntry->extension[j] == ' '; j--); + if (j >= 0) { + lfn->name[i++] = '.'; + lfn->name[i + j + 1] = '\0'; + for (;j >= 0; j--) { + if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f) + return -2; + else if (s->downcase_short_names) + lfn->name[i + j] = tolower(direntry->extension[j]); + else + lfn->name[i + j] = direntry->extension[j]; + } + } else + lfn->name[i + j + 1] = '\0'; + + lfn->len = strlen(lfn->name); + + return 0; +} + +static inline uint32_t modified_fat_get(BDRVVVFATState* s, + unsigned int cluster) +{ + if (cluster < s->last_cluster_of_root_directory) { + if (cluster + 1 == s->last_cluster_of_root_directory) + return s->max_fat_value; + else + return cluster + 1; + } + + if (s->fat_type==32) { + uint32_t* entry=((uint32_t*)s->fat2)+cluster; + return le32_to_cpu(*entry); + } else if (s->fat_type==16) { + uint16_t* entry=((uint16_t*)s->fat2)+cluster; + return le16_to_cpu(*entry); + } else { + const uint8_t* x=s->fat2+cluster*3/2; + return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff; + } +} + +static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num) +{ + int was_modified = 0; + int i, dummy; + + if (s->qcow == NULL) + return 0; + + for (i = 0; !was_modified && i < s->sectors_per_cluster; i++) + was_modified = s->qcow->drv->bdrv_is_allocated(s->qcow, + cluster2sector(s, cluster_num) + i, 1, &dummy); + + return was_modified; +} + +static const char* get_basename(const char* path) +{ + char* basename = strrchr(path, '/'); + if (basename == NULL) + return path; + else + return basename + 1; /* strip '/' */ +} + +/* + * The array s->used_clusters holds the states of the clusters. If it is + * part of a file, it has bit 2 set, in case of a directory, bit 1. If it + * was modified, bit 3 is set. + * If any cluster is allocated, but not part of a file or directory, this + * driver refuses to commit. + */ +typedef enum { + USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4 +} used_t; + +/* + * get_cluster_count_for_direntry() not only determines how many clusters + * are occupied by direntry, but also if it was renamed or modified. + * + * A file is thought to be renamed *only* if there already was a file with + * exactly the same first cluster, but a different name. + * + * Further, the files/directories handled by this function are + * assumed to be *not* deleted (and *only* those). + */ +static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, + direntry_t* direntry, const char* path) +{ + /* + * This is a little bit tricky: + * IF the guest OS just inserts a cluster into the file chain, + * and leaves the rest alone, (i.e. the original file had clusters + * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens: + * + * - do_commit will write the cluster into the file at the given + * offset, but + * + * - the cluster which is overwritten should be moved to a later + * position in the file. + * + * I am not aware that any OS does something as braindead, but this + * situation could happen anyway when not committing for a long time. + * Just to be sure that this does not bite us, detect it, and copy the + * contents of the clusters to-be-overwritten into the qcow. + */ + int copy_it = 0; + int was_modified = 0; + int32_t ret = 0; + + uint32_t cluster_num = begin_of_direntry(direntry); + uint32_t offset = 0; + int first_mapping_index = -1; + mapping_t* mapping = NULL; + const char* basename2 = NULL; + + vvfat_close_current_file(s); + + /* the root directory */ + if (cluster_num == 0) + return 0; + + /* write support */ + if (s->qcow) { + basename2 = get_basename(path); + + mapping = find_mapping_for_cluster(s, cluster_num); + + if (mapping) { + const char* basename; + + assert(mapping->mode & MODE_DELETED); + mapping->mode &= ~MODE_DELETED; + + basename = get_basename(mapping->path); + + assert(mapping->mode & MODE_NORMAL); + + /* rename */ + if (strcmp(basename, basename2)) + schedule_rename(s, cluster_num, strdup(path)); + } else if (is_file(direntry)) + /* new file */ + schedule_new_file(s, strdup(path), cluster_num); + else { + assert(0); + return 0; + } + } + + while(1) { + if (s->qcow) { + if (!copy_it && cluster_was_modified(s, cluster_num)) { + if (mapping == NULL || + mapping->begin > cluster_num || + mapping->end <= cluster_num) + mapping = find_mapping_for_cluster(s, cluster_num); + + + if (mapping && + (mapping->mode & MODE_DIRECTORY) == 0) { + + /* was modified in qcow */ + if (offset != mapping->info.file.offset + s->cluster_size + * (cluster_num - mapping->begin)) { + /* offset of this cluster in file chain has changed */ + assert(0); + copy_it = 1; + } else if (offset == 0) { + const char* basename = get_basename(mapping->path); + + if (strcmp(basename, basename2)) + copy_it = 1; + first_mapping_index = array_index(&(s->mapping), mapping); + } + + if (mapping->first_mapping_index != first_mapping_index + && mapping->info.file.offset > 0) { + assert(0); + copy_it = 1; + } + + /* need to write out? */ + if (!was_modified && is_file(direntry)) { + was_modified = 1; + schedule_writeout(s, mapping->dir_index, offset); + } + } + } + + if (copy_it) { + int i, dummy; + /* + * This is horribly inefficient, but that is okay, since + * it is rarely executed, if at all. + */ + int64_t offset = cluster2sector(s, cluster_num); + + vvfat_close_current_file(s); + for (i = 0; i < s->sectors_per_cluster; i++) + if (!s->qcow->drv->bdrv_is_allocated(s->qcow, + offset + i, 1, &dummy)) { + if (vvfat_read(s->bs, + offset, s->cluster_buffer, 1)) + return -1; + if (s->qcow->drv->bdrv_write(s->qcow, + offset, s->cluster_buffer, 1)) + return -2; + } + } + } + + ret++; + if (s->used_clusters[cluster_num] & USED_ANY) + return 0; + s->used_clusters[cluster_num] = USED_FILE; + + cluster_num = modified_fat_get(s, cluster_num); + + if (fat_eof(s, cluster_num)) + return ret; + else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16) + return -1; + + offset += s->cluster_size; + } +} + +/* + * This function looks at the modified data (qcow). + * It returns 0 upon inconsistency or error, and the number of clusters + * used by the directory, its subdirectories and their files. + */ +static int check_directory_consistency(BDRVVVFATState *s, + int cluster_num, const char* path) +{ + int ret = 0; + unsigned char* cluster = malloc(s->cluster_size); + direntry_t* direntries = (direntry_t*)cluster; + mapping_t* mapping = find_mapping_for_cluster(s, cluster_num); + + long_file_name lfn; + int path_len = strlen(path); + char path2[PATH_MAX]; + + assert(path_len < PATH_MAX); /* len was tested before! */ + strcpy(path2, path); + path2[path_len] = '/'; + path2[path_len + 1] = '\0'; + + if (mapping) { + const char* basename = get_basename(mapping->path); + const char* basename2 = get_basename(path); + + assert(mapping->mode & MODE_DIRECTORY); + + assert(mapping->mode & MODE_DELETED); + mapping->mode &= ~MODE_DELETED; + + if (strcmp(basename, basename2)) + schedule_rename(s, cluster_num, strdup(path)); + } else + /* new directory */ + schedule_mkdir(s, cluster_num, strdup(path)); + + lfn_init(&lfn); + do { + int i; + int subret = 0; + + ret++; + + if (s->used_clusters[cluster_num] & USED_ANY) { + fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num); + return 0; + } + s->used_clusters[cluster_num] = USED_DIRECTORY; + +DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num))); + subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster, + s->sectors_per_cluster); + if (subret) { + fprintf(stderr, "Error fetching direntries\n"); + fail: + free(cluster); + return 0; + } + + for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) { + int cluster_count; + +DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i)); + if (is_volume_label(direntries + i) || is_dot(direntries + i) || + is_free(direntries + i)) + continue; + + subret = parse_long_name(&lfn, direntries + i); + if (subret < 0) { + fprintf(stderr, "Error in long name\n"); + goto fail; + } + if (subret == 0 || is_free(direntries + i)) + continue; + + if (fat_chksum(direntries+i) != lfn.checksum) { + subret = parse_short_name(s, &lfn, direntries + i); + if (subret < 0) { + fprintf(stderr, "Error in short name (%d)\n", subret); + goto fail; + } + if (subret > 0 || !strcmp(lfn.name, ".") + || !strcmp(lfn.name, "..")) + continue; + } + lfn.checksum = 0x100; /* cannot use long name twice */ + + if (path_len + 1 + lfn.len >= PATH_MAX) { + fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name); + goto fail; + } + strcpy(path2 + path_len + 1, lfn.name); + + if (is_directory(direntries + i)) { + if (begin_of_direntry(direntries + i) == 0) { + DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i)); + goto fail; + } + cluster_count = check_directory_consistency(s, + begin_of_direntry(direntries + i), path2); + if (cluster_count == 0) { + DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i)); + goto fail; + } + } else if (is_file(direntries + i)) { + /* check file size with FAT */ + cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2); + if (cluster_count != + (le32_to_cpu(direntries[i].size) + s->cluster_size + - 1) / s->cluster_size) { + DLOG(fprintf(stderr, "Cluster count mismatch\n")); + goto fail; + } + } else + assert(0); /* cluster_count = 0; */ + + ret += cluster_count; + } + + cluster_num = modified_fat_get(s, cluster_num); + } while(!fat_eof(s, cluster_num)); + + free(cluster); + return ret; +} + +/* returns 1 on success */ +static int is_consistent(BDRVVVFATState* s) +{ + int i, check; + int used_clusters_count = 0; + +DLOG(checkpoint()); + /* + * - get modified FAT + * - compare the two FATs (TODO) + * - get buffer for marking used clusters + * - recurse direntries from root (using bs->bdrv_read to make + * sure to get the new data) + * - check that the FAT agrees with the size + * - count the number of clusters occupied by this directory and + * its files + * - check that the cumulative used cluster count agrees with the + * FAT + * - if all is fine, return number of used clusters + */ + if (s->fat2 == NULL) { + int size = 0x200 * s->sectors_per_fat; + s->fat2 = malloc(size); + memcpy(s->fat2, s->fat.pointer, size); + } + check = vvfat_read(s->bs, + s->first_sectors_number, s->fat2, s->sectors_per_fat); + if (check) { + fprintf(stderr, "Could not copy fat\n"); + return 0; + } + assert (s->used_clusters); + for (i = 0; i < sector2cluster(s, s->sector_count); i++) + s->used_clusters[i] &= ~USED_ANY; + + clear_commits(s); + + /* mark every mapped file/directory as deleted. + * (check_directory_consistency() will unmark those still present). */ + if (s->qcow) + for (i = 0; i < s->mapping.next; i++) { + mapping_t* mapping = array_get(&(s->mapping), i); + if (mapping->first_mapping_index < 0) + mapping->mode |= MODE_DELETED; + } + + used_clusters_count = check_directory_consistency(s, 0, s->path); + if (used_clusters_count <= 0) { + DLOG(fprintf(stderr, "problem in directory\n")); + return 0; + } + + check = s->last_cluster_of_root_directory; + for (i = check; i < sector2cluster(s, s->sector_count); i++) { + if (modified_fat_get(s, i)) { + if(!s->used_clusters[i]) { + DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i)); + return 0; + } + check++; + } + + if (s->used_clusters[i] == USED_ALLOCATED) { + /* allocated, but not used... */ + DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i)); + return 0; + } + } + + if (check != used_clusters_count) + return 0; + + return used_clusters_count; +} + +static inline void adjust_mapping_indices(BDRVVVFATState* s, + int offset, int adjust) +{ + int i; + + for (i = 0; i < s->mapping.next; i++) { + mapping_t* mapping = array_get(&(s->mapping), i); + +#define ADJUST_MAPPING_INDEX(name) \ + if (mapping->name >= offset) \ + mapping->name += adjust + + ADJUST_MAPPING_INDEX(first_mapping_index); + if (mapping->mode & MODE_DIRECTORY) + ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index); + } +} + +/* insert or update mapping */ +static mapping_t* insert_mapping(BDRVVVFATState* s, + uint32_t begin, uint32_t end) +{ + /* + * - find mapping where mapping->begin >= begin, + * - if mapping->begin > begin: insert + * - adjust all references to mappings! + * - else: adjust + * - replace name + */ + int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next); + mapping_t* mapping = NULL; + mapping_t* first_mapping = array_get(&(s->mapping), 0); + + if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index)) + && mapping->begin < begin) { + mapping->end = begin; + index++; + mapping = array_get(&(s->mapping), index); + } + if (index >= s->mapping.next || mapping->begin > begin) { + mapping = array_insert(&(s->mapping), index, 1); + mapping->path = NULL; + adjust_mapping_indices(s, index, +1); + } + + mapping->begin = begin; + mapping->end = end; + +DLOG(mapping_t* next_mapping; +assert(index + 1 >= s->mapping.next || +((next_mapping = array_get(&(s->mapping), index + 1)) && + next_mapping->begin >= end))); + + if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer) + s->current_mapping = array_get(&(s->mapping), + s->current_mapping - first_mapping); + + return mapping; +} + +static int remove_mapping(BDRVVVFATState* s, int mapping_index) +{ + mapping_t* mapping = array_get(&(s->mapping), mapping_index); + mapping_t* first_mapping = array_get(&(s->mapping), 0); + + /* free mapping */ + if (mapping->first_mapping_index < 0) + free(mapping->path); + + /* remove from s->mapping */ + array_remove(&(s->mapping), mapping_index); + + /* adjust all references to mappings */ + adjust_mapping_indices(s, mapping_index, -1); + + if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer) + s->current_mapping = array_get(&(s->mapping), + s->current_mapping - first_mapping); + + return 0; +} + +static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust) +{ + int i; + for (i = 0; i < s->mapping.next; i++) { + mapping_t* mapping = array_get(&(s->mapping), i); + if (mapping->dir_index >= offset) + mapping->dir_index += adjust; + if ((mapping->mode & MODE_DIRECTORY) && + mapping->info.dir.first_dir_index >= offset) + mapping->info.dir.first_dir_index += adjust; + } +} + +static direntry_t* insert_direntries(BDRVVVFATState* s, + int dir_index, int count) +{ + /* + * make room in s->directory, + * adjust_dirindices + */ + direntry_t* result = array_insert(&(s->directory), dir_index, count); + if (result == NULL) + return NULL; + adjust_dirindices(s, dir_index, count); + return result; +} + +static int remove_direntries(BDRVVVFATState* s, int dir_index, int count) +{ + int ret = array_remove_slice(&(s->directory), dir_index, count); + if (ret) + return ret; + adjust_dirindices(s, dir_index, -count); + return 0; +} + +/* + * Adapt the mappings of the cluster chain starting at first cluster + * (i.e. if a file starts at first_cluster, the chain is followed according + * to the modified fat, and the corresponding entries in s->mapping are + * adjusted) + */ +static int commit_mappings(BDRVVVFATState* s, + uint32_t first_cluster, int dir_index) +{ + mapping_t* mapping = find_mapping_for_cluster(s, first_cluster); + direntry_t* direntry = array_get(&(s->directory), dir_index); + uint32_t cluster = first_cluster; + + vvfat_close_current_file(s); + + assert(mapping); + assert(mapping->begin == first_cluster); + mapping->first_mapping_index = -1; + mapping->dir_index = dir_index; + mapping->mode = (dir_index <= 0 || is_directory(direntry)) ? + MODE_DIRECTORY : MODE_NORMAL; + + while (!fat_eof(s, cluster)) { + uint32_t c, c1; + + for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1; + c = c1, c1 = modified_fat_get(s, c1)); + + c++; + if (c > mapping->end) { + int index = array_index(&(s->mapping), mapping); + int i, max_i = s->mapping.next - index; + for (i = 1; i < max_i && mapping[i].begin < c; i++); + while (--i > 0) + remove_mapping(s, index + 1); + } + assert(mapping == array_get(&(s->mapping), s->mapping.next - 1) + || mapping[1].begin >= c); + mapping->end = c; + + if (!fat_eof(s, c1)) { + int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next); + mapping_t* next_mapping = i >= s->mapping.next ? NULL : + array_get(&(s->mapping), i); + + if (next_mapping == NULL || next_mapping->begin > c1) { + int i1 = array_index(&(s->mapping), mapping); + + next_mapping = insert_mapping(s, c1, c1+1); + + if (c1 < c) + i1++; + mapping = array_get(&(s->mapping), i1); + } + + next_mapping->dir_index = mapping->dir_index; + next_mapping->first_mapping_index = + mapping->first_mapping_index < 0 ? + array_index(&(s->mapping), mapping) : + mapping->first_mapping_index; + next_mapping->path = mapping->path; + next_mapping->mode = mapping->mode; + next_mapping->read_only = mapping->read_only; + if (mapping->mode & MODE_DIRECTORY) { + next_mapping->info.dir.parent_mapping_index = + mapping->info.dir.parent_mapping_index; + next_mapping->info.dir.first_dir_index = + mapping->info.dir.first_dir_index + + 0x10 * s->sectors_per_cluster * + (mapping->end - mapping->begin); + } else + next_mapping->info.file.offset = mapping->info.file.offset + + mapping->end - mapping->begin; + + mapping = next_mapping; + } + + cluster = c1; + } + + return 0; +} + +static int commit_direntries(BDRVVVFATState* s, + int dir_index, int parent_mapping_index) +{ + direntry_t* direntry = array_get(&(s->directory), dir_index); + uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry); + mapping_t* mapping = find_mapping_for_cluster(s, first_cluster); + + int factor = 0x10 * s->sectors_per_cluster; + int old_cluster_count, new_cluster_count; + int current_dir_index = mapping->info.dir.first_dir_index; + int first_dir_index = current_dir_index; + int ret, i; + uint32_t c; + +DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index)); + + assert(direntry); + assert(mapping); + assert(mapping->begin == first_cluster); + assert(mapping->info.dir.first_dir_index < s->directory.next); + assert(mapping->mode & MODE_DIRECTORY); + assert(dir_index == 0 || is_directory(direntry)); + + mapping->info.dir.parent_mapping_index = parent_mapping_index; + + if (first_cluster == 0) { + old_cluster_count = new_cluster_count = + s->last_cluster_of_root_directory; + } else { + for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c); + c = fat_get(s, c)) + old_cluster_count++; + + for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c); + c = modified_fat_get(s, c)) + new_cluster_count++; + } + + if (new_cluster_count > old_cluster_count) { + if (insert_direntries(s, + current_dir_index + factor * old_cluster_count, + factor * (new_cluster_count - old_cluster_count)) == NULL) + return -1; + } else if (new_cluster_count < old_cluster_count) + remove_direntries(s, + current_dir_index + factor * new_cluster_count, + factor * (old_cluster_count - new_cluster_count)); + + for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) { + void* direntry = array_get(&(s->directory), current_dir_index); + int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry, + s->sectors_per_cluster); + if (ret) + return ret; + assert(!strncmp(s->directory.pointer, "QEMU", 4)); + current_dir_index += factor; + } + + ret = commit_mappings(s, first_cluster, dir_index); + if (ret) + return ret; + + /* recurse */ + for (i = 0; i < factor * new_cluster_count; i++) { + direntry = array_get(&(s->directory), first_dir_index + i); + if (is_directory(direntry) && !is_dot(direntry)) { + mapping = find_mapping_for_cluster(s, first_cluster); + assert(mapping->mode & MODE_DIRECTORY); + ret = commit_direntries(s, first_dir_index + i, + array_index(&(s->mapping), mapping)); + if (ret) + return ret; + } + } + + return 0; +} + +/* commit one file (adjust contents, adjust mapping), + return first_mapping_index */ +static int commit_one_file(BDRVVVFATState* s, + int dir_index, uint32_t offset) +{ + direntry_t* direntry = array_get(&(s->directory), dir_index); + uint32_t c = begin_of_direntry(direntry); + uint32_t first_cluster = c; + mapping_t* mapping = find_mapping_for_cluster(s, c); + uint32_t size = filesize_of_direntry(direntry); + char* cluster = malloc(s->cluster_size); + uint32_t i; + int fd = 0; + + assert(offset < size); + assert((offset % s->cluster_size) == 0); + + for (i = s->cluster_size; i < offset; i += s->cluster_size) + c = modified_fat_get(s, c); + + fd = open(mapping->path, O_RDWR | O_CREAT, 0666); + if (fd < 0) { + fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path, + strerror(errno), errno); + return fd; + } + if (offset > 0) + if (lseek(fd, offset, SEEK_SET) != offset) + return -3; + + while (offset < size) { + uint32_t c1; + int rest_size = (size - offset > s->cluster_size ? + s->cluster_size : size - offset); + int ret; + + c1 = modified_fat_get(s, c); + + assert((size - offset == 0 && fat_eof(s, c)) || + (size > offset && c >=2 && !fat_eof(s, c))); + assert(size >= 0); + + ret = vvfat_read(s->bs, cluster2sector(s, c), + cluster, (rest_size + 0x1ff) / 0x200); + + if (ret < 0) + return ret; + + if (write(fd, cluster, rest_size) < 0) + return -2; + + offset += rest_size; + c = c1; + } + + ftruncate(fd, size); + close(fd); + + return commit_mappings(s, first_cluster, dir_index); +} + +#ifdef DEBUG +/* test, if all mappings point to valid direntries */ +static void check1(BDRVVVFATState* s) +{ + int i; + for (i = 0; i < s->mapping.next; i++) { + mapping_t* mapping = array_get(&(s->mapping), i); + if (mapping->mode & MODE_DELETED) { + fprintf(stderr, "deleted\n"); + continue; + } + assert(mapping->dir_index >= 0); + assert(mapping->dir_index < s->directory.next); + direntry_t* direntry = array_get(&(s->directory), mapping->dir_index); + assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0); + if (mapping->mode & MODE_DIRECTORY) { + assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next); + assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0); + } + } +} + +/* test, if all direntries have mappings */ +static void check2(BDRVVVFATState* s) +{ + int i; + int first_mapping = -1; + + for (i = 0; i < s->directory.next; i++) { + direntry_t* direntry = array_get(&(s->directory), i); + + if (is_short_name(direntry) && begin_of_direntry(direntry)) { + mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry)); + assert(mapping); + assert(mapping->dir_index == i || is_dot(direntry)); + assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry)); + } + + if ((i % (0x10 * s->sectors_per_cluster)) == 0) { + /* cluster start */ + int j, count = 0; + + for (j = 0; j < s->mapping.next; j++) { + mapping_t* mapping = array_get(&(s->mapping), j); + if (mapping->mode & MODE_DELETED) + continue; + if (mapping->mode & MODE_DIRECTORY) { + if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) { + assert(++count == 1); + if (mapping->first_mapping_index == -1) + first_mapping = array_index(&(s->mapping), mapping); + else + assert(first_mapping == mapping->first_mapping_index); + if (mapping->info.dir.parent_mapping_index < 0) + assert(j == 0); + else { + mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index); + assert(parent->mode & MODE_DIRECTORY); + assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index); + } + } + } + } + if (count == 0) + first_mapping = -1; + } + } +} +#endif + +static int handle_renames_and_mkdirs(BDRVVVFATState* s) +{ + int i; + +#ifdef DEBUG + fprintf(stderr, "handle_renames\n"); + for (i = 0; i < s->commits.next; i++) { + commit_t* commit = array_get(&(s->commits), i); + fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action); + } +#endif + + for (i = 0; i < s->commits.next;) { + commit_t* commit = array_get(&(s->commits), i); + if (commit->action == ACTION_RENAME) { + mapping_t* mapping = find_mapping_for_cluster(s, + commit->param.rename.cluster); + char* old_path = mapping->path; + + assert(commit->path); + mapping->path = commit->path; + if (rename(old_path, mapping->path)) + return -2; + + if (mapping->mode & MODE_DIRECTORY) { + int l1 = strlen(mapping->path); + int l2 = strlen(old_path); + int diff = l1 - l2; + direntry_t* direntry = array_get(&(s->directory), + mapping->info.dir.first_dir_index); + uint32_t c = mapping->begin; + int i = 0; + + /* recurse */ + while (!fat_eof(s, c)) { + do { + direntry_t* d = direntry + i; + + if (is_file(d) || (is_directory(d) && !is_dot(d))) { + mapping_t* m = find_mapping_for_cluster(s, + begin_of_direntry(d)); + int l = strlen(m->path); + char* new_path = malloc(l + diff + 1); + + assert(!strncmp(m->path, mapping->path, l2)); + + strcpy(new_path, mapping->path); + strcpy(new_path + l1, m->path + l2); + + schedule_rename(s, m->begin, new_path); + } + i++; + } while((i % (0x10 * s->sectors_per_cluster)) != 0); + c = fat_get(s, c); + } + } + + free(old_path); + array_remove(&(s->commits), i); + continue; + } else if (commit->action == ACTION_MKDIR) { + mapping_t* mapping; + int j, parent_path_len; + +#ifdef __MINGW32__ + if (mkdir(commit->path)) + return -5; +#else + if (mkdir(commit->path, 0755)) + return -5; +#endif + + mapping = insert_mapping(s, commit->param.mkdir.cluster, + commit->param.mkdir.cluster + 1); + if (mapping == NULL) + return -6; + + mapping->mode = MODE_DIRECTORY; + mapping->read_only = 0; + mapping->path = commit->path; + j = s->directory.next; + assert(j); + insert_direntries(s, s->directory.next, + 0x10 * s->sectors_per_cluster); + mapping->info.dir.first_dir_index = j; + + parent_path_len = strlen(commit->path) + - strlen(get_basename(commit->path)) - 1; + for (j = 0; j < s->mapping.next; j++) { + mapping_t* m = array_get(&(s->mapping), j); + if (m->first_mapping_index < 0 && m != mapping && + !strncmp(m->path, mapping->path, parent_path_len) && + strlen(m->path) == parent_path_len) + break; + } + assert(j < s->mapping.next); + mapping->info.dir.parent_mapping_index = j; + + array_remove(&(s->commits), i); + continue; + } + + i++; + } + return 0; +} + +/* + * TODO: make sure that the short name is not matching *another* file + */ +static int handle_commits(BDRVVVFATState* s) +{ + int i, fail = 0; + + vvfat_close_current_file(s); + + for (i = 0; !fail && i < s->commits.next; i++) { + commit_t* commit = array_get(&(s->commits), i); + switch(commit->action) { + case ACTION_RENAME: case ACTION_MKDIR: + assert(0); + fail = -2; + break; + case ACTION_WRITEOUT: { + direntry_t* entry = array_get(&(s->directory), + commit->param.writeout.dir_index); + uint32_t begin = begin_of_direntry(entry); + mapping_t* mapping = find_mapping_for_cluster(s, begin); + + assert(mapping); + assert(mapping->begin == begin); + assert(commit->path == NULL); + + if (commit_one_file(s, commit->param.writeout.dir_index, + commit->param.writeout.modified_offset)) + fail = -3; + + break; + } + case ACTION_NEW_FILE: { + int begin = commit->param.new_file.first_cluster; + mapping_t* mapping = find_mapping_for_cluster(s, begin); + direntry_t* entry; + int i; + + /* find direntry */ + for (i = 0; i < s->directory.next; i++) { + entry = array_get(&(s->directory), i); + if (is_file(entry) && begin_of_direntry(entry) == begin) + break; + } + + if (i >= s->directory.next) { + fail = -6; + continue; + } + + /* make sure there exists an initial mapping */ + if (mapping && mapping->begin != begin) { + mapping->end = begin; + mapping = NULL; + } + if (mapping == NULL) { + mapping = insert_mapping(s, begin, begin+1); + } + /* most members will be fixed in commit_mappings() */ + assert(commit->path); + mapping->path = commit->path; + mapping->read_only = 0; + mapping->mode = MODE_NORMAL; + mapping->info.file.offset = 0; + + if (commit_one_file(s, i, 0)) + fail = -7; + + break; + } + default: + assert(0); + } + } + if (i > 0 && array_remove_slice(&(s->commits), 0, i)) + return -1; + return fail; +} + +static int handle_deletes(BDRVVVFATState* s) +{ + int i, deferred = 1, deleted = 1; + + /* delete files corresponding to mappings marked as deleted */ + /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */ + while (deferred && deleted) { + deferred = 0; + deleted = 0; + + for (i = 1; i < s->mapping.next; i++) { + mapping_t* mapping = array_get(&(s->mapping), i); + if (mapping->mode & MODE_DELETED) { + direntry_t* entry = array_get(&(s->directory), + mapping->dir_index); + + if (is_free(entry)) { + /* remove file/directory */ + if (mapping->mode & MODE_DIRECTORY) { + int j, next_dir_index = s->directory.next, + first_dir_index = mapping->info.dir.first_dir_index; + + if (rmdir(mapping->path) < 0) { + if (errno == ENOTEMPTY) { + deferred++; + continue; + } else + return -5; + } + + for (j = 1; j < s->mapping.next; j++) { + mapping_t* m = array_get(&(s->mapping), j); + if (m->mode & MODE_DIRECTORY && + m->info.dir.first_dir_index > + first_dir_index && + m->info.dir.first_dir_index < + next_dir_index) + next_dir_index = + m->info.dir.first_dir_index; + } + remove_direntries(s, first_dir_index, + next_dir_index - first_dir_index); + + deleted++; + } + } else { + if (unlink(mapping->path)) + return -4; + deleted++; + } + DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry)); + remove_mapping(s, i); + } + } + } + + return 0; +} + +/* + * synchronize mapping with new state: + * + * - copy FAT (with bdrv_read) + * - mark all filenames corresponding to mappings as deleted + * - recurse direntries from root (using bs->bdrv_read) + * - delete files corresponding to mappings marked as deleted + */ +static int do_commit(BDRVVVFATState* s) +{ + int ret = 0; + + /* the real meat are the commits. Nothing to do? Move along! */ + if (s->commits.next == 0) + return 0; + + vvfat_close_current_file(s); + + ret = handle_renames_and_mkdirs(s); + if (ret) { + fprintf(stderr, "Error handling renames (%d)\n", ret); + assert(0); + return ret; + } + + /* copy FAT (with bdrv_read) */ + memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat); + + /* recurse direntries from root (using bs->bdrv_read) */ + ret = commit_direntries(s, 0, -1); + if (ret) { + fprintf(stderr, "Fatal: error while committing (%d)\n", ret); + assert(0); + return ret; + } + + ret = handle_commits(s); + if (ret) { + fprintf(stderr, "Error handling commits (%d)\n", ret); + assert(0); + return ret; + } + + ret = handle_deletes(s); + if (ret) { + fprintf(stderr, "Error deleting\n"); + assert(0); + return ret; + } + + s->qcow->drv->bdrv_make_empty(s->qcow); + + memset(s->used_clusters, 0, sector2cluster(s, s->sector_count)); + +DLOG(checkpoint()); + return 0; +} + +static int try_commit(BDRVVVFATState* s) +{ + vvfat_close_current_file(s); +DLOG(checkpoint()); + if(!is_consistent(s)) + return -1; + return do_commit(s); +} + +static int vvfat_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BDRVVVFATState *s = bs->opaque; + int i, ret; + +DLOG(checkpoint()); + + vvfat_close_current_file(s); + + /* + * Some sanity checks: + * - do not allow writing to the boot sector + * - do not allow to write non-ASCII filenames + */ + + if (sector_num < s->first_sectors_number) + return -1; + + for (i = sector2cluster(s, sector_num); + i <= sector2cluster(s, sector_num + nb_sectors - 1);) { + mapping_t* mapping = find_mapping_for_cluster(s, i); + if (mapping) { + if (mapping->read_only) { + fprintf(stderr, "Tried to write to write-protected file %s\n", + mapping->path); + return -1; + } + + if (mapping->mode & MODE_DIRECTORY) { + int begin = cluster2sector(s, i); + int end = begin + s->sectors_per_cluster, k; + int dir_index; + const direntry_t* direntries; + long_file_name lfn; + + lfn_init(&lfn); + + if (begin < sector_num) + begin = sector_num; + if (end > sector_num + nb_sectors) + end = sector_num + nb_sectors; + dir_index = mapping->dir_index + + 0x10 * (begin - mapping->begin * s->sectors_per_cluster); + direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num)); + + for (k = 0; k < (end - begin) * 0x10; k++) { + /* do not allow non-ASCII filenames */ + if (parse_long_name(&lfn, direntries + k) < 0) { + fprintf(stderr, "Warning: non-ASCII filename\n"); + return -1; + } + /* no access to the direntry of a read-only file */ + else if (is_short_name(direntries+k) && + (direntries[k].attributes & 1)) { + if (memcmp(direntries + k, + array_get(&(s->directory), dir_index + k), + sizeof(direntry_t))) { + fprintf(stderr, "Warning: tried to write to write-protected file\n"); + return -1; + } + } + } + } + i = mapping->end; + } else + i++; + } + + /* + * Use qcow backend. Commit later. + */ +DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors)); + ret = s->qcow->drv->bdrv_write(s->qcow, sector_num, buf, nb_sectors); + if (ret < 0) { + fprintf(stderr, "Error writing to qcow backend\n"); + return ret; + } + + for (i = sector2cluster(s, sector_num); + i <= sector2cluster(s, sector_num + nb_sectors - 1); i++) + if (i >= 0) + s->used_clusters[i] |= USED_ALLOCATED; + +DLOG(checkpoint()); + /* TODO: add timeout */ + try_commit(s); + +DLOG(checkpoint()); + return 0; +} + +static int vvfat_is_allocated(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int* n) +{ + BDRVVVFATState* s = bs->opaque; + *n = s->sector_count - sector_num; + if (*n > nb_sectors) + *n = nb_sectors; + else if (*n < 0) + return 0; + return 1; +} + +static int write_target_commit(BlockDriverState *bs, int64_t sector_num, + const uint8_t* buffer, int nb_sectors) { + BDRVVVFATState* s = bs->opaque; + return try_commit(s); +} + +static void write_target_close(BlockDriverState *bs) { + BDRVVVFATState* s = bs->opaque; + bdrv_delete(s->qcow); + free(s->qcow_filename); +} + +static BlockDriver vvfat_write_target = { + "vvfat_write_target", 0, NULL, NULL, NULL, + write_target_commit, + write_target_close, + NULL, NULL, NULL +}; + +static int enable_write_target(BDRVVVFATState *s) +{ + int size = sector2cluster(s, s->sector_count); + s->used_clusters = calloc(size, 1); + + array_init(&(s->commits), sizeof(commit_t)); + + s->qcow_filename = malloc(1024); + strcpy(s->qcow_filename, "/tmp/vl.XXXXXX"); + get_tmp_filename(s->qcow_filename, strlen(s->qcow_filename) + 1); + if (bdrv_create(&bdrv_qcow, + s->qcow_filename, s->sector_count, "fat:", 0) < 0) + return -1; + s->qcow = bdrv_new(""); + if (s->qcow == NULL || bdrv_open(s->qcow, s->qcow_filename, 0) < 0) + return -1; + +#ifndef _WIN32 + unlink(s->qcow_filename); +#endif + + s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1); + s->bs->backing_hd->drv = &vvfat_write_target; + s->bs->backing_hd->opaque = s; + + return 0; +} + +static void vvfat_close(BlockDriverState *bs) +{ + BDRVVVFATState *s = bs->opaque; + + vvfat_close_current_file(s); + array_free(&(s->fat)); + array_free(&(s->directory)); + array_free(&(s->mapping)); + if(s->cluster_buffer) + free(s->cluster_buffer); +} + +BlockDriver bdrv_vvfat = { + "vvfat", + sizeof(BDRVVVFATState), + vvfat_probe, + vvfat_open, + vvfat_read, + vvfat_write, + vvfat_close, + NULL, + vvfat_is_allocated +}; + +#ifdef DEBUG +static void checkpoint() { + assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2); + check1(vvv); + check2(vvv); + assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY)); +#if 0 + if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf) + fprintf(stderr, "Nonono!\n"); + mapping_t* mapping; + direntry_t* direntry; + assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next); + assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next); + if (vvv->mapping.next<47) + return; + assert((mapping = array_get(&(vvv->mapping), 47))); + assert(mapping->dir_index < vvv->directory.next); + direntry = array_get(&(vvv->directory), mapping->dir_index); + assert(!memcmp(direntry->name, "USB H ", 11) || direntry->name[0]==0); +#endif + return; + /* avoid compiler warnings: */ + hexdump(NULL, 100); + remove_mapping(vvv, NULL); + print_mapping(NULL); + print_direntry(NULL); +} +#endif + diff --git a/tools/ioemu/block.c b/tools/ioemu/block.c new file mode 100644 index 0000000000..b908167186 --- /dev/null +++ b/tools/ioemu/block.c @@ -0,0 +1,797 @@ +/* + * QEMU System Emulator block driver + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "block_int.h" + +#ifdef _BSD +#include +#include +#include +#include +#include +#endif + +#ifdef CONFIG_COCOA +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#endif + +#ifdef __sun__ +#include +#endif + +static BlockDriverState *bdrv_first; +static BlockDriver *first_drv; + +#ifdef CONFIG_COCOA +static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ); +static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ); + +kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator ) +{ + kern_return_t kernResult; + mach_port_t masterPort; + CFMutableDictionaryRef classesToMatch; + + kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort ); + if ( KERN_SUCCESS != kernResult ) { + printf( "IOMasterPort returned %d\n", kernResult ); + } + + classesToMatch = IOServiceMatching( kIOCDMediaClass ); + if ( classesToMatch == NULL ) { + printf( "IOServiceMatching returned a NULL dictionary.\n" ); + } else { + CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue ); + } + kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator ); + if ( KERN_SUCCESS != kernResult ) + { + printf( "IOServiceGetMatchingServices returned %d\n", kernResult ); + } + + return kernResult; +} + +kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize ) +{ + io_object_t nextMedia; + kern_return_t kernResult = KERN_FAILURE; + *bsdPath = '\0'; + nextMedia = IOIteratorNext( mediaIterator ); + if ( nextMedia ) + { + CFTypeRef bsdPathAsCFString; + bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 ); + if ( bsdPathAsCFString ) { + size_t devPathLength; + strcpy( bsdPath, _PATH_DEV ); + strcat( bsdPath, "r" ); + devPathLength = strlen( bsdPath ); + if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) { + kernResult = KERN_SUCCESS; + } + CFRelease( bsdPathAsCFString ); + } + IOObjectRelease( nextMedia ); + } + + return kernResult; +} + +#endif + +void bdrv_register(BlockDriver *bdrv) +{ + bdrv->next = first_drv; + first_drv = bdrv; +} + +/* create a new block device (by default it is empty) */ +BlockDriverState *bdrv_new(const char *device_name) +{ + BlockDriverState **pbs, *bs; + + bs = qemu_mallocz(sizeof(BlockDriverState)); + if(!bs) + return NULL; + pstrcpy(bs->device_name, sizeof(bs->device_name), device_name); + if (device_name[0] != '\0') { + /* insert at the end */ + pbs = &bdrv_first; + while (*pbs != NULL) + pbs = &(*pbs)->next; + *pbs = bs; + } + return bs; +} + +BlockDriver *bdrv_find_format(const char *format_name) +{ + BlockDriver *drv1; + for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { + if (!strcmp(drv1->format_name, format_name)) + return drv1; + } + return NULL; +} + +int bdrv_create(BlockDriver *drv, + const char *filename, int64_t size_in_sectors, + const char *backing_file, int flags) +{ + if (!drv->bdrv_create) + return -ENOTSUP; + return drv->bdrv_create(filename, size_in_sectors, backing_file, flags); +} + +#ifdef _WIN32 +void get_tmp_filename(char *filename, int size) +{ + char* p = strrchr(filename, '/'); + + if (p == NULL) + return; + + /* XXX: find a better function */ + tmpnam(p); + *p = '/'; +} +#else +void get_tmp_filename(char *filename, int size) +{ + int fd; + /* XXX: race condition possible */ + pstrcpy(filename, size, "/tmp/vl.XXXXXX"); + fd = mkstemp(filename); + close(fd); +} +#endif + +/* XXX: force raw format if block or character device ? It would + simplify the BSD case */ +static BlockDriver *find_image_format(const char *filename) +{ + int fd, ret, score, score_max; + BlockDriver *drv1, *drv; + uint8_t *buf; + size_t bufsize = 1024; + + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + if (fd < 0) { + buf = NULL; + ret = 0; + } else { +#ifdef DIOCGSECTORSIZE + { + unsigned int sectorsize = 512; + if (!ioctl(fd, DIOCGSECTORSIZE, §orsize) && + sectorsize > bufsize) + bufsize = sectorsize; + } +#endif +#ifdef CONFIG_COCOA + u_int32_t blockSize = 512; + if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) { + bufsize = blockSize; + } +#endif + buf = qemu_malloc(bufsize); + if (!buf) + return NULL; + ret = read(fd, buf, bufsize); + if (ret < 0) { + close(fd); + qemu_free(buf); + return NULL; + } + close(fd); + } + + drv = NULL; + score_max = 0; + for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { + score = drv1->bdrv_probe(buf, ret, filename); + if (score > score_max) { + score_max = score; + drv = drv1; + } + } + qemu_free(buf); + return drv; +} + +int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot) +{ +#ifdef CONFIG_COCOA + if ( strncmp( filename, "/dev/cdrom", 10 ) == 0 ) { + kern_return_t kernResult; + io_iterator_t mediaIterator; + char bsdPath[ MAXPATHLEN ]; + int fd; + + kernResult = FindEjectableCDMedia( &mediaIterator ); + kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) ); + + if ( bsdPath[ 0 ] != '\0' ) { + strcat(bsdPath,"s0"); + /* some CDs don't have a partition 0 */ + fd = open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE); + if (fd < 0) { + bsdPath[strlen(bsdPath)-1] = '1'; + } else { + close(fd); + } + filename = bsdPath; + } + + if ( mediaIterator ) + IOObjectRelease( mediaIterator ); + } +#endif + return bdrv_open2(bs, filename, snapshot, NULL); +} + +int bdrv_open2(BlockDriverState *bs, const char *filename, int snapshot, + BlockDriver *drv) +{ + int ret; + char tmp_filename[1024]; + + bs->read_only = 0; + bs->is_temporary = 0; + bs->encrypted = 0; + + if (snapshot) { + BlockDriverState *bs1; + int64_t total_size; + + /* if snapshot, we create a temporary backing file and open it + instead of opening 'filename' directly */ + + /* if there is a backing file, use it */ + bs1 = bdrv_new(""); + if (!bs1) { + return -1; + } + if (bdrv_open(bs1, filename, 0) < 0) { + bdrv_delete(bs1); + return -1; + } + total_size = bs1->total_sectors; + bdrv_delete(bs1); + + get_tmp_filename(tmp_filename, sizeof(tmp_filename)); + /* XXX: use cow for linux as it is more efficient ? */ + if (bdrv_create(&bdrv_qcow, tmp_filename, + total_size, filename, 0) < 0) { + return -1; + } + filename = tmp_filename; + bs->is_temporary = 1; + } + + pstrcpy(bs->filename, sizeof(bs->filename), filename); + if (!drv) { + drv = find_image_format(filename); + if (!drv) + return -1; + } + bs->drv = drv; + bs->opaque = qemu_mallocz(drv->instance_size); + if (bs->opaque == NULL && drv->instance_size > 0) + return -1; + + ret = drv->bdrv_open(bs, filename); + if (ret < 0) { + qemu_free(bs->opaque); + return -1; + } +#ifndef _WIN32 + if (bs->is_temporary) { + unlink(filename); + } +#endif + if (bs->backing_file[0] != '\0' && drv->bdrv_is_allocated) { + /* if there is a backing file, use it */ + bs->backing_hd = bdrv_new(""); + if (!bs->backing_hd) { + fail: + bdrv_close(bs); + return -1; + } + if (bdrv_open(bs->backing_hd, bs->backing_file, 0) < 0) + goto fail; + } + + bs->inserted = 1; + + /* call the change callback */ + if (bs->change_cb) + bs->change_cb(bs->change_opaque); + + return 0; +} + +void bdrv_close(BlockDriverState *bs) +{ + if (bs->inserted) { + if (bs->backing_hd) + bdrv_delete(bs->backing_hd); + bs->drv->bdrv_close(bs); + qemu_free(bs->opaque); +#ifdef _WIN32 + if (bs->is_temporary) { + unlink(bs->filename); + } +#endif + bs->opaque = NULL; + bs->drv = NULL; + bs->inserted = 0; + + /* call the change callback */ + if (bs->change_cb) + bs->change_cb(bs->change_opaque); + } +} + +void bdrv_delete(BlockDriverState *bs) +{ + /* XXX: remove the driver list */ + bdrv_close(bs); + qemu_free(bs); +} + +/* commit COW file into the raw image */ +int bdrv_commit(BlockDriverState *bs) +{ + int64_t i; + int n, j; + unsigned char sector[512]; + + if (!bs->inserted) + return -ENOENT; + + if (bs->read_only) { + return -EACCES; + } + + if (!bs->backing_hd) { + return -ENOTSUP; + } + + for (i = 0; i < bs->total_sectors;) { + if (bs->drv->bdrv_is_allocated(bs, i, 65536, &n)) { + for(j = 0; j < n; j++) { + if (bdrv_read(bs, i, sector, 1) != 0) { + return -EIO; + } + + if (bdrv_write(bs->backing_hd, i, sector, 1) != 0) { + return -EIO; + } + i++; + } + } else { + i += n; + } + } + + if (bs->drv->bdrv_make_empty) + return bs->drv->bdrv_make_empty(bs); + + return 0; +} + +/* return -1 if error */ +int bdrv_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + int ret, n; + BlockDriver *drv = bs->drv; + + if (!bs->inserted) + return -1; + + while (nb_sectors > 0) { + if (sector_num == 0 && bs->boot_sector_enabled) { + memcpy(buf, bs->boot_sector_data, 512); + n = 1; + } else if (bs->backing_hd) { + if (drv->bdrv_is_allocated(bs, sector_num, nb_sectors, &n)) { + ret = drv->bdrv_read(bs, sector_num, buf, n); + if (ret < 0) + return -1; + } else { + /* read from the base image */ + ret = bdrv_read(bs->backing_hd, sector_num, buf, n); + if (ret < 0) + return -1; + } + } else { + ret = drv->bdrv_read(bs, sector_num, buf, nb_sectors); + if (ret < 0) + return -1; + /* no need to loop */ + break; + } + nb_sectors -= n; + sector_num += n; + buf += n * 512; + } + return 0; +} + +/* return -1 if error */ +int bdrv_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + if (!bs->inserted) + return -1; + if (bs->read_only) + return -1; + if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) { + memcpy(bs->boot_sector_data, buf, 512); + } + return bs->drv->bdrv_write(bs, sector_num, buf, nb_sectors); +} + +void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr) +{ + *nb_sectors_ptr = bs->total_sectors; +} + +/* force a given boot sector. */ +void bdrv_set_boot_sector(BlockDriverState *bs, const uint8_t *data, int size) +{ + bs->boot_sector_enabled = 1; + if (size > 512) + size = 512; + memcpy(bs->boot_sector_data, data, size); + memset(bs->boot_sector_data + size, 0, 512 - size); +} + +void bdrv_set_geometry_hint(BlockDriverState *bs, + int cyls, int heads, int secs) +{ + bs->cyls = cyls; + bs->heads = heads; + bs->secs = secs; +} + +void bdrv_set_type_hint(BlockDriverState *bs, int type) +{ + bs->type = type; + bs->removable = ((type == BDRV_TYPE_CDROM || + type == BDRV_TYPE_FLOPPY)); +} + +void bdrv_set_translation_hint(BlockDriverState *bs, int translation) +{ + bs->translation = translation; +} + +void bdrv_get_geometry_hint(BlockDriverState *bs, + int *pcyls, int *pheads, int *psecs) +{ + *pcyls = bs->cyls; + *pheads = bs->heads; + *psecs = bs->secs; +} + +int bdrv_get_type_hint(BlockDriverState *bs) +{ + return bs->type; +} + +int bdrv_get_translation_hint(BlockDriverState *bs) +{ + return bs->translation; +} + +int bdrv_is_removable(BlockDriverState *bs) +{ + return bs->removable; +} + +int bdrv_is_read_only(BlockDriverState *bs) +{ + return bs->read_only; +} + +int bdrv_is_inserted(BlockDriverState *bs) +{ + return bs->inserted; +} + +int bdrv_is_locked(BlockDriverState *bs) +{ + return bs->locked; +} + +void bdrv_set_locked(BlockDriverState *bs, int locked) +{ + bs->locked = locked; +} + +void bdrv_set_change_cb(BlockDriverState *bs, + void (*change_cb)(void *opaque), void *opaque) +{ + bs->change_cb = change_cb; + bs->change_opaque = opaque; +} + +int bdrv_is_encrypted(BlockDriverState *bs) +{ + if (bs->backing_hd && bs->backing_hd->encrypted) + return 1; + return bs->encrypted; +} + +int bdrv_set_key(BlockDriverState *bs, const char *key) +{ + int ret; + if (bs->backing_hd && bs->backing_hd->encrypted) { + ret = bdrv_set_key(bs->backing_hd, key); + if (ret < 0) + return ret; + if (!bs->encrypted) + return 0; + } + if (!bs->encrypted || !bs->drv || !bs->drv->bdrv_set_key) + return -1; + return bs->drv->bdrv_set_key(bs, key); +} + +void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size) +{ + if (!bs->inserted || !bs->drv) { + buf[0] = '\0'; + } else { + pstrcpy(buf, buf_size, bs->drv->format_name); + } +} + +void bdrv_iterate_format(void (*it)(void *opaque, const char *name), + void *opaque) +{ + BlockDriver *drv; + + for (drv = first_drv; drv != NULL; drv = drv->next) { + it(opaque, drv->format_name); + } +} + +BlockDriverState *bdrv_find(const char *name) +{ + BlockDriverState *bs; + + for (bs = bdrv_first; bs != NULL; bs = bs->next) { + if (!strcmp(name, bs->device_name)) + return bs; + } + return NULL; +} + +void bdrv_iterate(void (*it)(void *opaque, const char *name), void *opaque) +{ + BlockDriverState *bs; + + for (bs = bdrv_first; bs != NULL; bs = bs->next) { + it(opaque, bs->device_name); + } +} + +const char *bdrv_get_device_name(BlockDriverState *bs) +{ + return bs->device_name; +} + +void bdrv_info(void) +{ + BlockDriverState *bs; + + for (bs = bdrv_first; bs != NULL; bs = bs->next) { + term_printf("%s:", bs->device_name); + term_printf(" type="); + switch(bs->type) { + case BDRV_TYPE_HD: + term_printf("hd"); + break; + case BDRV_TYPE_CDROM: + term_printf("cdrom"); + break; + case BDRV_TYPE_FLOPPY: + term_printf("floppy"); + break; + } + term_printf(" removable=%d", bs->removable); + if (bs->removable) { + term_printf(" locked=%d", bs->locked); + } + if (bs->inserted) { + term_printf(" file=%s", bs->filename); + if (bs->backing_file[0] != '\0') + term_printf(" backing_file=%s", bs->backing_file); + term_printf(" ro=%d", bs->read_only); + term_printf(" drv=%s", bs->drv->format_name); + if (bs->encrypted) + term_printf(" encrypted"); + } else { + term_printf(" [not inserted]"); + } + term_printf("\n"); + } +} + +/**************************************************************/ +/* RAW block driver */ + +typedef struct BDRVRawState { + int fd; +} BDRVRawState; + +static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + return 1; /* maybe */ +} + +static int raw_open(BlockDriverState *bs, const char *filename) +{ + BDRVRawState *s = bs->opaque; + int fd; + int64_t size; +#ifdef _BSD + struct stat sb; +#endif +#ifdef __sun__ + struct dk_minfo minfo; + int rv; +#endif + + fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); + if (fd < 0) { + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + if (fd < 0) + return -1; + bs->read_only = 1; + } +#ifdef _BSD + if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) { +#ifdef DIOCGMEDIASIZE + if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size)) +#endif +#ifdef CONFIG_COCOA + size = LONG_LONG_MAX; +#else + size = lseek(fd, 0LL, SEEK_END); +#endif + } else +#endif +#ifdef __sun__ + /* + * use the DKIOCGMEDIAINFO ioctl to read the size. + */ + rv = ioctl ( fd, DKIOCGMEDIAINFO, &minfo ); + if ( rv != -1 ) { + size = minfo.dki_lbsize * minfo.dki_capacity; + } else /* there are reports that lseek on some devices + fails, but irc discussion said that contingency + on contingency was overkill */ +#endif + { + size = lseek(fd, 0, SEEK_END); + } +#ifdef _WIN32 + /* On Windows hosts it can happen that we're unable to get file size + for CD-ROM raw device (it's inherent limitation of the CDFS driver). */ + if (size == -1) + size = LONG_LONG_MAX; +#endif + bs->total_sectors = size / 512; + s->fd = fd; + return 0; +} + +static int raw_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVRawState *s = bs->opaque; + int ret; + + lseek(s->fd, sector_num * 512, SEEK_SET); + ret = read(s->fd, buf, nb_sectors * 512); + if (ret != nb_sectors * 512) + return -1; + return 0; +} + +static int raw_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BDRVRawState *s = bs->opaque; + int ret; + + lseek(s->fd, sector_num * 512, SEEK_SET); + ret = write(s->fd, buf, nb_sectors * 512); + if (ret != nb_sectors * 512) + return -1; + return 0; +} + +static void raw_close(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + close(s->fd); +} + +static int raw_create(const char *filename, int64_t total_size, + const char *backing_file, int flags) +{ + int fd; + + if (flags || backing_file) + return -ENOTSUP; + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, + 0644); + if (fd < 0) + return -EIO; + ftruncate(fd, total_size * 512); + close(fd); + return 0; +} + +BlockDriver bdrv_raw = { + "raw", + sizeof(BDRVRawState), + raw_probe, + raw_open, + raw_read, + raw_write, + raw_close, + raw_create, +}; + +void bdrv_init(void) +{ + bdrv_register(&bdrv_raw); +#ifndef _WIN32 + bdrv_register(&bdrv_cow); +#endif + bdrv_register(&bdrv_qcow); + bdrv_register(&bdrv_vmdk); + bdrv_register(&bdrv_cloop); + bdrv_register(&bdrv_dmg); + bdrv_register(&bdrv_bochs); + bdrv_register(&bdrv_vpc); + bdrv_register(&bdrv_vvfat); +} diff --git a/tools/ioemu/block_int.h b/tools/ioemu/block_int.h new file mode 100644 index 0000000000..e3038160e6 --- /dev/null +++ b/tools/ioemu/block_int.h @@ -0,0 +1,80 @@ +/* + * QEMU System Emulator block driver + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef BLOCK_INT_H +#define BLOCK_INT_H + +struct BlockDriver { + const char *format_name; + int instance_size; + int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename); + int (*bdrv_open)(BlockDriverState *bs, const char *filename); + int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors); + int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors); + void (*bdrv_close)(BlockDriverState *bs); + int (*bdrv_create)(const char *filename, int64_t total_sectors, + const char *backing_file, int flags); + int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum); + int (*bdrv_set_key)(BlockDriverState *bs, const char *key); + int (*bdrv_make_empty)(BlockDriverState *bs); + struct BlockDriver *next; +}; + +struct BlockDriverState { + int64_t total_sectors; + int read_only; /* if true, the media is read only */ + int inserted; /* if true, the media is present */ + int removable; /* if true, the media can be removed */ + int locked; /* if true, the media cannot temporarily be ejected */ + int encrypted; /* if true, the media is encrypted */ + /* event callback when inserting/removing */ + void (*change_cb)(void *opaque); + void *change_opaque; + + BlockDriver *drv; + void *opaque; + + int boot_sector_enabled; + uint8_t boot_sector_data[512]; + + char filename[1024]; + char backing_file[1024]; /* if non zero, the image is a diff of + this file image */ + int is_temporary; + + BlockDriverState *backing_hd; + + /* NOTE: the following infos are only hints for real hardware + drivers. They are not used by the block driver */ + int cyls, heads, secs, translation; + int type; + char device_name[32]; + BlockDriverState *next; +}; + +void get_tmp_filename(char *filename, int size); + +#endif /* BLOCK_INT_H */ diff --git a/tools/ioemu/bswap.h b/tools/ioemu/bswap.h new file mode 100644 index 0000000000..37fb04ed97 --- /dev/null +++ b/tools/ioemu/bswap.h @@ -0,0 +1,202 @@ +#ifndef BSWAP_H +#define BSWAP_H + +#include "config-host.h" + +#include + +#ifdef HAVE_BYTESWAP_H +#include +#else + +#define bswap_16(x) \ +({ \ + uint16_t __x = (x); \ + ((uint16_t)( \ + (((uint16_t)(__x) & (uint16_t)0x00ffU) << 8) | \ + (((uint16_t)(__x) & (uint16_t)0xff00U) >> 8) )); \ +}) + +#define bswap_32(x) \ +({ \ + uint32_t __x = (x); \ + ((uint32_t)( \ + (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \ +}) + +#define bswap_64(x) \ +({ \ + uint64_t __x = (x); \ + ((uint64_t)( \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \ +}) + +#endif /* !HAVE_BYTESWAP_H */ + +static inline uint16_t bswap16(uint16_t x) +{ + return bswap_16(x); +} + +static inline uint32_t bswap32(uint32_t x) +{ + return bswap_32(x); +} + +static inline uint64_t bswap64(uint64_t x) +{ + return bswap_64(x); +} + +static inline void bswap16s(uint16_t *s) +{ + *s = bswap16(*s); +} + +static inline void bswap32s(uint32_t *s) +{ + *s = bswap32(*s); +} + +static inline void bswap64s(uint64_t *s) +{ + *s = bswap64(*s); +} + +#if defined(WORDS_BIGENDIAN) +#define be_bswap(v, size) (v) +#define le_bswap(v, size) bswap ## size(v) +#define be_bswaps(v, size) +#define le_bswaps(p, size) *p = bswap ## size(*p); +#else +#define le_bswap(v, size) (v) +#define be_bswap(v, size) bswap ## size(v) +#define le_bswaps(v, size) +#define be_bswaps(p, size) *p = bswap ## size(*p); +#endif + +#define CPU_CONVERT(endian, size, type)\ +static inline type endian ## size ## _to_cpu(type v)\ +{\ + return endian ## _bswap(v, size);\ +}\ +\ +static inline type cpu_to_ ## endian ## size(type v)\ +{\ + return endian ## _bswap(v, size);\ +}\ +\ +static inline void endian ## size ## _to_cpus(type *p)\ +{\ + endian ## _bswaps(p, size)\ +}\ +\ +static inline void cpu_to_ ## endian ## size ## s(type *p)\ +{\ + endian ## _bswaps(p, size)\ +}\ +\ +static inline type endian ## size ## _to_cpup(const type *p)\ +{\ + return endian ## size ## _to_cpu(*p);\ +}\ +\ +static inline void cpu_to_ ## endian ## size ## w(type *p, type v)\ +{\ + *p = cpu_to_ ## endian ## size(v);\ +} + +CPU_CONVERT(be, 16, uint16_t) +CPU_CONVERT(be, 32, uint32_t) +CPU_CONVERT(be, 64, uint64_t) + +CPU_CONVERT(le, 16, uint16_t) +CPU_CONVERT(le, 32, uint32_t) +CPU_CONVERT(le, 64, uint64_t) + +/* unaligned versions (optimized for frequent unaligned accesses)*/ + +#if defined(__i386__) || defined(__powerpc__) + +#define cpu_to_le16wu(p, v) cpu_to_le16w(p, v) +#define cpu_to_le32wu(p, v) cpu_to_le32w(p, v) +#define le16_to_cpupu(p) le16_to_cpup(p) +#define le32_to_cpupu(p) le32_to_cpup(p) + +#define cpu_to_be16wu(p, v) cpu_to_be16w(p, v) +#define cpu_to_be32wu(p, v) cpu_to_be32w(p, v) + +#else + +static inline void cpu_to_le16wu(uint16_t *p, uint16_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v; + p1[1] = v >> 8; +} + +static inline void cpu_to_le32wu(uint32_t *p, uint32_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v; + p1[1] = v >> 8; + p1[2] = v >> 16; + p1[3] = v >> 24; +} + +static inline uint16_t le16_to_cpupu(const uint16_t *p) +{ + const uint8_t *p1 = (const uint8_t *)p; + return p1[0] | (p1[1] << 8); +} + +static inline uint32_t le32_to_cpupu(const uint32_t *p) +{ + const uint8_t *p1 = (const uint8_t *)p; + return p1[0] | (p1[1] << 8) | (p1[2] << 16) | (p1[3] << 24); +} + +static inline void cpu_to_be16wu(uint16_t *p, uint16_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v >> 8; + p1[1] = v; +} + +static inline void cpu_to_be32wu(uint32_t *p, uint32_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v >> 24; + p1[1] = v >> 16; + p1[2] = v >> 8; + p1[3] = v; +} + +#endif + +#ifdef WORDS_BIGENDIAN +#define cpu_to_32wu cpu_to_be32wu +#else +#define cpu_to_32wu cpu_to_le32wu +#endif + +#undef le_bswap +#undef be_bswap +#undef le_bswaps +#undef be_bswaps + +#endif /* BSWAP_H */ diff --git a/tools/ioemu/cocoa.m b/tools/ioemu/cocoa.m new file mode 100644 index 0000000000..b508b52144 --- /dev/null +++ b/tools/ioemu/cocoa.m @@ -0,0 +1,911 @@ +/* + * QEMU Cocoa display driver + * + * Copyright (c) 2005 Pierre d'Herbemont + * many code/inspiration from SDL 1.2 code (LGPL) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* + Todo : x miniaturize window + x center the window + - save window position + - handle keyboard event + - handle mouse event + - non 32 bpp support + - full screen + - mouse focus + x simple graphical prompt to demo + - better graphical prompt +*/ + +#import + +#include "vl.h" + +NSWindow *window = NULL; +NSQuickDrawView *qd_view = NULL; + + +int gArgc; +char **gArgv; +DisplayState current_ds; + +int grab = 0; +int modifiers_state[256]; + +/* main defined in qemu/vl.c */ +int qemu_main(int argc, char **argv); + +/* To deal with miniaturization */ +@interface QemuWindow : NSWindow +{ } +@end + + +/* + ------------------------------------------------------ + Qemu Video Driver + ------------------------------------------------------ +*/ + +/* + ------------------------------------------------------ + cocoa_update + ------------------------------------------------------ +*/ +static void cocoa_update(DisplayState *ds, int x, int y, int w, int h) +{ + //printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h); + + /* Use QDFlushPortBuffer() to flush content to display */ + RgnHandle dirty = NewRgn (); + RgnHandle temp = NewRgn (); + + SetEmptyRgn (dirty); + + /* Build the region of dirty rectangles */ + MacSetRectRgn (temp, x, y, + x + w, y + h); + MacUnionRgn (dirty, temp, dirty); + + /* Flush the dirty region */ + QDFlushPortBuffer ( [ qd_view qdPort ], dirty ); + DisposeRgn (dirty); + DisposeRgn (temp); +} + +/* + ------------------------------------------------------ + cocoa_resize + ------------------------------------------------------ +*/ +static void cocoa_resize(DisplayState *ds, int w, int h) +{ + const int device_bpp = 32; + static void *screen_pixels; + static int screen_pitch; + NSRect contentRect; + + //printf("resizing to %d %d\n", w, h); + + contentRect = NSMakeRect (0, 0, w, h); + if(window) + { + [window close]; + [window release]; + } + window = [ [ QemuWindow alloc ] initWithContentRect:contentRect + styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask + backing:NSBackingStoreBuffered defer:NO]; + if(!window) + { + fprintf(stderr, "(cocoa) can't create window\n"); + exit(1); + } + + if(qd_view) + [qd_view release]; + + qd_view = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ]; + + if(!qd_view) + { + fprintf(stderr, "(cocoa) can't create qd_view\n"); + exit(1); + } + + [ window setAcceptsMouseMovedEvents:YES ]; + [ window setTitle:@"Qemu" ]; + [ window setReleasedWhenClosed:NO ]; + + /* Set screen to black */ + [ window setBackgroundColor: [NSColor blackColor] ]; + + /* set window position */ + [ window center ]; + + [ qd_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ]; + [ [ window contentView ] addSubview:qd_view ]; + [ qd_view release ]; + [ window makeKeyAndOrderFront:nil ]; + + /* Careful here, the window seems to have to be onscreen to do that */ + LockPortBits ( [ qd_view qdPort ] ); + screen_pixels = GetPixBaseAddr ( GetPortPixMap ( [ qd_view qdPort ] ) ); + screen_pitch = GetPixRowBytes ( GetPortPixMap ( [ qd_view qdPort ] ) ); + UnlockPortBits ( [ qd_view qdPort ] ); + { + int vOffset = [ window frame ].size.height - + [ qd_view frame ].size.height - [ qd_view frame ].origin.y; + + int hOffset = [ qd_view frame ].origin.x; + + screen_pixels += (vOffset * screen_pitch) + hOffset * (device_bpp/8); + } + ds->data = screen_pixels; + ds->linesize = screen_pitch; + ds->depth = device_bpp; + ds->width = w; + ds->height = h; + + current_ds = *ds; +} + +/* + ------------------------------------------------------ + keymap conversion + ------------------------------------------------------ +*/ + +int keymap[] = +{ +// SdlI macI macH SdlH 104xtH 104xtC sdl + 30, // 0 0x00 0x1e A QZ_a + 31, // 1 0x01 0x1f S QZ_s + 32, // 2 0x02 0x20 D QZ_d + 33, // 3 0x03 0x21 F QZ_f + 35, // 4 0x04 0x23 H QZ_h + 34, // 5 0x05 0x22 G QZ_g + 44, // 6 0x06 0x2c Z QZ_z + 45, // 7 0x07 0x2d X QZ_x + 46, // 8 0x08 0x2e C QZ_c + 47, // 9 0x09 0x2f V QZ_v + 0, // 10 0x0A Undefined + 48, // 11 0x0B 0x30 B QZ_b + 16, // 12 0x0C 0x10 Q QZ_q + 17, // 13 0x0D 0x11 W QZ_w + 18, // 14 0x0E 0x12 E QZ_e + 19, // 15 0x0F 0x13 R QZ_r + 21, // 16 0x10 0x15 Y QZ_y + 20, // 17 0x11 0x14 T QZ_t + 2, // 18 0x12 0x02 1 QZ_1 + 3, // 19 0x13 0x03 2 QZ_2 + 4, // 20 0x14 0x04 3 QZ_3 + 5, // 21 0x15 0x05 4 QZ_4 + 7, // 22 0x16 0x07 6 QZ_6 + 6, // 23 0x17 0x06 5 QZ_5 + 13, // 24 0x18 0x0d = QZ_EQUALS + 10, // 25 0x19 0x0a 9 QZ_9 + 8, // 26 0x1A 0x08 7 QZ_7 + 12, // 27 0x1B 0x0c - QZ_MINUS + 9, // 28 0x1C 0x09 8 QZ_8 + 11, // 29 0x1D 0x0b 0 QZ_0 + 27, // 30 0x1E 0x1b ] QZ_RIGHTBRACKET + 24, // 31 0x1F 0x18 O QZ_o + 22, // 32 0x20 0x16 U QZ_u + 26, // 33 0x21 0x1a [ QZ_LEFTBRACKET + 23, // 34 0x22 0x17 I QZ_i + 25, // 35 0x23 0x19 P QZ_p + 28, // 36 0x24 0x1c ENTER QZ_RETURN + 38, // 37 0x25 0x26 L QZ_l + 36, // 38 0x26 0x24 J QZ_j + 40, // 39 0x27 0x28 ' QZ_QUOTE + 37, // 40 0x28 0x25 K QZ_k + 39, // 41 0x29 0x27 ; QZ_SEMICOLON + 43, // 42 0x2A 0x2b \ QZ_BACKSLASH + 51, // 43 0x2B 0x33 , QZ_COMMA + 53, // 44 0x2C 0x35 / QZ_SLASH + 49, // 45 0x2D 0x31 N QZ_n + 50, // 46 0x2E 0x32 M QZ_m + 52, // 47 0x2F 0x34 . QZ_PERIOD + 15, // 48 0x30 0x0f TAB QZ_TAB + 57, // 49 0x31 0x39 SPACE QZ_SPACE + 41, // 50 0x32 0x29 ` QZ_BACKQUOTE + 14, // 51 0x33 0x0e BKSP QZ_BACKSPACE + 0, // 52 0x34 Undefined + 1, // 53 0x35 0x01 ESC QZ_ESCAPE + 0, // 54 0x36 QZ_RMETA + 0, // 55 0x37 QZ_LMETA + 42, // 56 0x38 0x2a L SHFT QZ_LSHIFT + 58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK + 56, // 58 0x3A 0x38 L ALT QZ_LALT + 29, // 59 0x3B 0x1d L CTRL QZ_LCTRL + 54, // 60 0x3C 0x36 R SHFT QZ_RSHIFT + 184,// 61 0x3D 0xb8 E0,38 R ALT QZ_RALT + 157,// 62 0x3E 0x9d E0,1D R CTRL QZ_RCTRL + 0, // 63 0x3F Undefined + 0, // 64 0x40 Undefined + 0, // 65 0x41 Undefined + 0, // 66 0x42 Undefined + 55, // 67 0x43 0x37 KP * QZ_KP_MULTIPLY + 0, // 68 0x44 Undefined + 78, // 69 0x45 0x4e KP + QZ_KP_PLUS + 0, // 70 0x46 Undefined + 69, // 71 0x47 0x45 NUM QZ_NUMLOCK + 0, // 72 0x48 Undefined + 0, // 73 0x49 Undefined + 0, // 74 0x4A Undefined + 181,// 75 0x4B 0xb5 E0,35 KP / QZ_KP_DIVIDE + 152,// 76 0x4C 0x9c E0,1C KP EN QZ_KP_ENTER + 0, // 77 0x4D undefined + 74, // 78 0x4E 0x4a KP - QZ_KP_MINUS + 0, // 79 0x4F Undefined + 0, // 80 0x50 Undefined + 0, // 81 0x51 QZ_KP_EQUALS + 82, // 82 0x52 0x52 KP 0 QZ_KP0 + 79, // 83 0x53 0x4f KP 1 QZ_KP1 + 80, // 84 0x54 0x50 KP 2 QZ_KP2 + 81, // 85 0x55 0x51 KP 3 QZ_KP3 + 75, // 86 0x56 0x4b KP 4 QZ_KP4 + 76, // 87 0x57 0x4c KP 5 QZ_KP5 + 77, // 88 0x58 0x4d KP 6 QZ_KP6 + 71, // 89 0x59 0x47 KP 7 QZ_KP7 + 0, // 90 0x5A Undefined + 72, // 91 0x5B 0x48 KP 8 QZ_KP8 + 73, // 92 0x5C 0x49 KP 9 QZ_KP9 + 0, // 93 0x5D Undefined + 0, // 94 0x5E Undefined + 0, // 95 0x5F Undefined + 63, // 96 0x60 0x3f F5 QZ_F5 + 64, // 97 0x61 0x40 F6 QZ_F6 + 65, // 98 0x62 0x41 F7 QZ_F7 + 61, // 99 0x63 0x3d F3 QZ_F3 + 66, // 100 0x64 0x42 F8 QZ_F8 + 67, // 101 0x65 0x43 F9 QZ_F9 + 0, // 102 0x66 Undefined + 87, // 103 0x67 0x57 F11 QZ_F11 + 0, // 104 0x68 Undefined + 183,// 105 0x69 0xb7 QZ_PRINT + 0, // 106 0x6A Undefined + 70, // 107 0x6B 0x46 SCROLL QZ_SCROLLOCK + 0, // 108 0x6C Undefined + 68, // 109 0x6D 0x44 F10 QZ_F10 + 0, // 110 0x6E Undefined + 88, // 111 0x6F 0x58 F12 QZ_F12 + 0, // 112 0x70 Undefined + 110,// 113 0x71 0x0 QZ_PAUSE + 210,// 114 0x72 0xd2 E0,52 INSERT QZ_INSERT + 199,// 115 0x73 0xc7 E0,47 HOME QZ_HOME + 201,// 116 0x74 0xc9 E0,49 PG UP QZ_PAGEUP + 211,// 117 0x75 0xd3 E0,53 DELETE QZ_DELETE + 62, // 118 0x76 0x3e F4 QZ_F4 + 207,// 119 0x77 0xcf E0,4f END QZ_END + 60, // 120 0x78 0x3c F2 QZ_F2 + 209,// 121 0x79 0xd1 E0,51 PG DN QZ_PAGEDOWN + 59, // 122 0x7A 0x3b F1 QZ_F1 + 203,// 123 0x7B 0xcb e0,4B L ARROW QZ_LEFT + 205,// 124 0x7C 0xcd e0,4D R ARROW QZ_RIGHT + 208,// 125 0x7D 0xd0 E0,50 D ARROW QZ_DOWN + 200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP +/* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */ + +/* Aditional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */ +/* + 219 // 0xdb e0,5b L GUI + 220 // 0xdc e0,5c R GUI + 221 // 0xdd e0,5d APPS + // E0,2A,E0,37 PRNT SCRN + // E1,1D,45,E1,9D,C5 PAUSE + 83 // 0x53 0x53 KP . +// ACPI Scan Codes + 222 // 0xde E0, 5E Power + 223 // 0xdf E0, 5F Sleep + 227 // 0xe3 E0, 63 Wake +// Windows Multimedia Scan Codes + 153 // 0x99 E0, 19 Next Track + 144 // 0x90 E0, 10 Previous Track + 164 // 0xa4 E0, 24 Stop + 162 // 0xa2 E0, 22 Play/Pause + 160 // 0xa0 E0, 20 Mute + 176 // 0xb0 E0, 30 Volume Up + 174 // 0xae E0, 2E Volume Down + 237 // 0xed E0, 6D Media Select + 236 // 0xec E0, 6C E-Mail + 161 // 0xa1 E0, 21 Calculator + 235 // 0xeb E0, 6B My Computer + 229 // 0xe5 E0, 65 WWW Search + 178 // 0xb2 E0, 32 WWW Home + 234 // 0xea E0, 6A WWW Back + 233 // 0xe9 E0, 69 WWW Forward + 232 // 0xe8 E0, 68 WWW Stop + 231 // 0xe7 E0, 67 WWW Refresh + 230 // 0xe6 E0, 66 WWW Favorites +*/ +}; + +int cocoa_keycode_to_qemu(int keycode) +{ + if((sizeof(keymap)/sizeof(int)) <= keycode) + { + printf("(cocoa) warning unknow keycode 0x%x\n", keycode); + return 0; + } + return keymap[keycode]; +} + +/* + ------------------------------------------------------ + cocoa_refresh + ------------------------------------------------------ +*/ +static void cocoa_refresh(DisplayState *ds) +{ + //printf("cocoa_refresh \n"); + NSDate *distantPast; + NSEvent *event; + NSAutoreleasePool *pool; + + pool = [ [ NSAutoreleasePool alloc ] init ]; + distantPast = [ NSDate distantPast ]; + + vga_hw_update(); + + do { + event = [ NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast + inMode: NSDefaultRunLoopMode dequeue:YES ]; + if (event != nil) { + switch ([event type]) { + case NSFlagsChanged: + { + int keycode = cocoa_keycode_to_qemu([event keyCode]); + + if (keycode) + { + if (keycode == 58 || keycode == 69) { + /* emulate caps lock and num lock keydown and keyup */ + kbd_put_keycode(keycode); + kbd_put_keycode(keycode | 0x80); + } else if (is_graphic_console()) { + if (keycode & 0x80) + kbd_put_keycode(0xe0); + if (modifiers_state[keycode] == 0) { + /* keydown */ + kbd_put_keycode(keycode & 0x7f); + modifiers_state[keycode] = 1; + } else { + /* keyup */ + kbd_put_keycode(keycode | 0x80); + modifiers_state[keycode] = 0; + } + } + } + + /* release Mouse grab when pressing ctrl+alt */ + if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) + { + [window setTitle: @"QEMU"]; + [NSCursor unhide]; + CGAssociateMouseAndMouseCursorPosition ( TRUE ); + grab = 0; + } + } + break; + + case NSKeyDown: + { + int keycode = cocoa_keycode_to_qemu([event keyCode]); + + /* handle command Key Combos */ + if ([event modifierFlags] & NSCommandKeyMask) { + switch ([event keyCode]) { + /* quit */ + case 12: /* q key */ + /* switch to windowed View */ + exit(0); + return; + } + } + + /* handle control + alt Key Combos */ + if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) { + switch (keycode) { + /* toggle Monitor */ + case 0x02 ... 0x0a: /* '1' to '9' keys */ + console_select(keycode - 0x02); + break; + } + } else { + /* handle standard key events */ + if (is_graphic_console()) { + if (keycode & 0x80) //check bit for e0 in front + kbd_put_keycode(0xe0); + kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front + /* handle monitor key events */ + } else { + switch([event keyCode]) { + case 123: + kbd_put_keysym(QEMU_KEY_LEFT); + break; + case 124: + kbd_put_keysym(QEMU_KEY_RIGHT); + break; + case 125: + kbd_put_keysym(QEMU_KEY_DOWN); + break; + case 126: + kbd_put_keysym(QEMU_KEY_UP); + break; + default: + kbd_put_keysym([[event characters] characterAtIndex:0]); + break; + } + } + } + } + break; + + case NSKeyUp: + { + int keycode = cocoa_keycode_to_qemu([event keyCode]); + if (is_graphic_console()) { + if (keycode & 0x80) + kbd_put_keycode(0xe0); + kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key + } + } + break; + + case NSMouseMoved: + if (grab) { + int dx = [event deltaX]; + int dy = [event deltaY]; + int dz = [event deltaZ]; + int buttons = 0; + kbd_mouse_event(dx, dy, dz, buttons); + } + break; + + case NSLeftMouseDown: + if (grab) { + int buttons = 0; + + /* leftclick+command simulates rightclick */ + if ([event modifierFlags] & NSCommandKeyMask) { + buttons |= MOUSE_EVENT_RBUTTON; + } else { + buttons |= MOUSE_EVENT_LBUTTON; + } + kbd_mouse_event(0, 0, 0, buttons); + } else { + [NSApp sendEvent: event]; + } + break; + + case NSLeftMouseDragged: + if (grab) { + int dx = [event deltaX]; + int dy = [event deltaY]; + int dz = [event deltaZ]; + int buttons = 0; + if ([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) { //leftclick+command simulates rightclick + buttons |= MOUSE_EVENT_RBUTTON; + } else { + buttons |= MOUSE_EVENT_LBUTTON; + } + kbd_mouse_event(dx, dy, dz, buttons); + } + break; + + case NSLeftMouseUp: + if (grab) { + kbd_mouse_event(0, 0, 0, 0); + } else { + [window setTitle: @"QEMU (Press ctrl + alt to release Mouse)"]; + [NSCursor hide]; + CGAssociateMouseAndMouseCursorPosition ( FALSE ); + grab = 1; + //[NSApp sendEvent: event]; + } + break; + + case NSRightMouseDown: + if (grab) { + int buttons = 0; + + buttons |= MOUSE_EVENT_RBUTTON; + kbd_mouse_event(0, 0, 0, buttons); + } else { + [NSApp sendEvent: event]; + } + break; + + case NSRightMouseDragged: + if (grab) { + int dx = [event deltaX]; + int dy = [event deltaY]; + int dz = [event deltaZ]; + int buttons = 0; + buttons |= MOUSE_EVENT_RBUTTON; + kbd_mouse_event(dx, dy, dz, buttons); + } + break; + + case NSRightMouseUp: + if (grab) { + kbd_mouse_event(0, 0, 0, 0); + } else { + [NSApp sendEvent: event]; + } + break; + + case NSOtherMouseDragged: + if (grab) { + int dx = [event deltaX]; + int dy = [event deltaY]; + int dz = [event deltaZ]; + int buttons = 0; + buttons |= MOUSE_EVENT_MBUTTON; + kbd_mouse_event(dx, dy, dz, buttons); + } + break; + + case NSOtherMouseDown: + if (grab) { + int buttons = 0; + buttons |= MOUSE_EVENT_MBUTTON; + kbd_mouse_event(0, 0, 0, buttons); + } else { + [NSApp sendEvent:event]; + } + break; + + case NSOtherMouseUp: + if (grab) { + kbd_mouse_event(0, 0, 0, 0); + } else { + [NSApp sendEvent: event]; + } + break; + + case NSScrollWheel: + if (grab) { + int dz = [event deltaY]; + kbd_mouse_event(0, 0, -dz, 0); + } + break; + + default: [NSApp sendEvent:event]; + } + } + } while(event != nil); +} + +/* + ------------------------------------------------------ + cocoa_cleanup + ------------------------------------------------------ +*/ + +static void cocoa_cleanup(void) +{ + +} + +/* + ------------------------------------------------------ + cocoa_display_init + ------------------------------------------------------ +*/ + +void cocoa_display_init(DisplayState *ds, int full_screen) +{ + ds->dpy_update = cocoa_update; + ds->dpy_resize = cocoa_resize; + ds->dpy_refresh = cocoa_refresh; + + cocoa_resize(ds, 640, 400); + + atexit(cocoa_cleanup); +} + +/* + ------------------------------------------------------ + Interface with Cocoa + ------------------------------------------------------ +*/ + + +/* + ------------------------------------------------------ + QemuWindow + Some trick from SDL to use miniwindow + ------------------------------------------------------ +*/ +static void QZ_SetPortAlphaOpaque () +{ + /* Assume 32 bit if( bpp == 32 )*/ + if ( 1 ) { + + uint32_t *pixels = (uint32_t*) current_ds.data; + uint32_t rowPixels = current_ds.linesize / 4; + uint32_t i, j; + + for (i = 0; i < current_ds.height; i++) + for (j = 0; j < current_ds.width; j++) { + + pixels[ (i * rowPixels) + j ] |= 0xFF000000; + } + } +} + +@implementation QemuWindow +- (void)miniaturize:(id)sender +{ + + /* make the alpha channel opaque so anim won't have holes in it */ + QZ_SetPortAlphaOpaque (); + + [ super miniaturize:sender ]; + +} +- (void)display +{ + /* + This method fires just before the window deminaturizes from the Dock. + + We'll save the current visible surface, let the window manager redraw any + UI elements, and restore the SDL surface. This way, no expose event + is required, and the deminiaturize works perfectly. + */ + + /* make sure pixels are fully opaque */ + QZ_SetPortAlphaOpaque (); + + /* save current visible SDL surface */ + [ self cacheImageInRect:[ qd_view frame ] ]; + + /* let the window manager redraw controls, border, etc */ + [ super display ]; + + /* restore visible SDL surface */ + [ self restoreCachedImage ]; +} + +@end + + +/* + ------------------------------------------------------ + QemuCocoaGUIController + NSApp's delegate - indeed main object + ------------------------------------------------------ +*/ + +@interface QemuCocoaGUIController : NSObject +{ +} +- (void)applicationDidFinishLaunching: (NSNotification *) note; +- (void)applicationWillTerminate:(NSNotification *)aNotification; + +- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo; + +- (void)startEmulationWithArgc:(int)argc argv:(char**)argv; +@end + +@implementation QemuCocoaGUIController +/* Called when the internal event loop has just started running */ +- (void)applicationDidFinishLaunching: (NSNotification *) note +{ + + /* Display an open dialog box if no argument were passed or + if qemu was launched from the finder ( the Finder passes "-psn" ) */ + + if( gArgc <= 1 || strncmp (gArgv[1], "-psn", 4) == 0) + { + NSOpenPanel *op = [[NSOpenPanel alloc] init]; + + cocoa_resize(¤t_ds, 640, 400); + + [op setPrompt:@"Boot image"]; + + [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"]; + + [op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil] + modalForWindow:window modalDelegate:self + didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL]; + } + else + { + /* or Launch Qemu, with the global args */ + [self startEmulationWithArgc:gArgc argv:gArgv]; + } +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification +{ + printf("Application will terminate\n"); + qemu_system_shutdown_request(); + /* In order to avoid a crash */ + exit(0); +} + +- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + if(returnCode == NSCancelButton) + { + exit(0); + } + + if(returnCode == NSOKButton) + { + char *bin = "qemu"; + char *img = (char*)[ [ sheet filename ] cString]; + + char **argv = (char**)malloc( sizeof(char*)*3 ); + + asprintf(&argv[0], "%s", bin); + asprintf(&argv[1], "-hda"); + asprintf(&argv[2], "%s", img); + + printf("Using argc %d argv %s -hda %s\n", 3, bin, img); + + [self startEmulationWithArgc:3 argv:(char**)argv]; + } +} + +- (void)startEmulationWithArgc:(int)argc argv:(char**)argv +{ + int status; + /* Launch Qemu */ + printf("starting qemu...\n"); + status = qemu_main (argc, argv); + exit(status); +} +@end + +/* + ------------------------------------------------------ + Application Creation + ------------------------------------------------------ +*/ + +/* Dock Connection */ +typedef struct CPSProcessSerNum +{ + UInt32 lo; + UInt32 hi; +} CPSProcessSerNum; + +extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); +extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); +extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); + +/* Menu Creation */ +static void setApplicationMenu(void) +{ + /* warning: this code is very odd */ + NSMenu *appleMenu; + NSMenuItem *menuItem; + NSString *title; + NSString *appName; + + appName = @"Qemu"; + appleMenu = [[NSMenu alloc] initWithTitle:@""]; + + /* Add menu items */ + title = [@"About " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Hide " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; + + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Quit " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + + + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:appleMenu]; + [[NSApp mainMenu] addItem:menuItem]; + + /* Tell the application object that this is now the application menu */ + [NSApp setAppleMenu:appleMenu]; + + /* Finally give up our references to the objects */ + [appleMenu release]; + [menuItem release]; +} + +/* Create a window menu */ +static void setupWindowMenu(void) +{ + NSMenu *windowMenu; + NSMenuItem *windowMenuItem; + NSMenuItem *menuItem; + + windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + + /* "Minimize" item */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; + [windowMenu addItem:menuItem]; + [menuItem release]; + + /* Put menu into the menubar */ + windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; + [windowMenuItem setSubmenu:windowMenu]; + [[NSApp mainMenu] addItem:windowMenuItem]; + + /* Tell the application object that this is now the window menu */ + [NSApp setWindowsMenu:windowMenu]; + + /* Finally give up our references to the objects */ + [windowMenu release]; + [windowMenuItem release]; + +} + +static void CustomApplicationMain (argc, argv) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + QemuCocoaGUIController *gui_controller; + CPSProcessSerNum PSN; + + [NSApplication sharedApplication]; + + if (!CPSGetCurrentProcess(&PSN)) + if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) + if (!CPSSetFrontProcess(&PSN)) + [NSApplication sharedApplication]; + + /* Set up the menubar */ + [NSApp setMainMenu:[[NSMenu alloc] init]]; + setApplicationMenu(); + setupWindowMenu(); + + /* Create SDLMain and make it the app delegate */ + gui_controller = [[QemuCocoaGUIController alloc] init]; + [NSApp setDelegate:gui_controller]; + + /* Start the main event loop */ + [NSApp run]; + + [gui_controller release]; + [pool release]; +} + +/* Real main of qemu-cocoa */ +int main(int argc, char **argv) +{ + gArgc = argc; + gArgv = argv; + + CustomApplicationMain (argc, argv); + + return 0; +} diff --git a/tools/ioemu/configure b/tools/ioemu/configure new file mode 100644 index 0000000000..24b829826b --- /dev/null +++ b/tools/ioemu/configure @@ -0,0 +1,896 @@ +#!/bin/sh +# +# qemu configure script (c) 2003 Fabrice Bellard +# +# set temporary file name +if test ! -z "$TMPDIR" ; then + TMPDIR1="${TMPDIR}" +elif test ! -z "$TEMPDIR" ; then + TMPDIR1="${TEMPDIR}" +else + TMPDIR1="/tmp" +fi + +TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c" +TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o" +TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}" +TMPS="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.S" + +# default parameters +prefix="" +static="no" +libdir="lib" +cross_prefix="" +cc="gcc" +host_cc="gcc" +ar="ar" +make="make" +install="install" +strip="strip" +cpu=`uname -m` +target_list="" +case "$cpu" in + i386|i486|i586|i686|i86pc|BePC) + cpu="i386" + ;; + armv*b) + cpu="armv4b" + ;; + armv*l) + cpu="armv4l" + ;; + alpha) + cpu="alpha" + ;; + "Power Macintosh"|ppc|ppc64) + cpu="powerpc" + ;; + mips) + cpu="mips" + ;; + s390) + cpu="s390" + ;; + sparc|sun4[muv]) + cpu="sparc" + ;; + sparc64) + cpu="sparc64" + ;; + ia64) + cpu="ia64" + ;; + m68k) + cpu="m68k" + ;; + x86_64|amd64) + cpu="x86_64" + libdir="lib64" + ;; + *) + cpu="unknown" + ;; +esac +gprof="no" +bigendian="no" +mingw32="no" +EXESUF="" +gdbstub="no" +slirp="no" +adlib="no" +oss="no" +dsound="no" +coreaudio="no" +alsa="no" +fmod="no" +fmod_lib="" +fmod_inc="" +bsd="no" +linux="no" +kqemu="no" +profiler="no" +kernel_path="" +cocoa="no" +check_gfx="yes" +check_gcc="no" +softmmu="yes" +user="no" +build_docs="no" + +# OS specific +targetos=`uname -s` +case $targetos in +CYGWIN*) +mingw32="yes" +CFLAGS="-O2 -mno-cygwin" +;; +MINGW32*) +mingw32="yes" +;; +FreeBSD) +bsd="yes" +oss="yes" +if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then + kqemu="yes" +fi +;; +NetBSD) +bsd="yes" +oss="yes" +;; +OpenBSD) +bsd="yes" +oss="yes" +;; +Darwin) +bsd="yes" +darwin="yes" +;; +SunOS) +solaris="yes" +;; +*) +oss="yes" +linux="yes" +user="yes" +if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then + kqemu="yes" +fi +;; +esac + +if [ "$bsd" = "yes" ] ; then + if [ "$darwin" != "yes" ] ; then + make="gmake" + fi +fi + +if [ "$solaris" = "yes" ] ; then + make="gmake" + install="ginstall" + solarisrev=`uname -r | cut -f2 -d.` +fi + +# find source path +source_path=`dirname "$0"` +if [ -z "$source_path" ]; then + source_path=`pwd` +else + source_path=`cd "$source_path"; pwd` +fi +if test "$source_path" = `pwd` ; then + source_path_used="no" +else + source_path_used="yes" +fi + +for opt do + optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'` + case "$opt" in + --help|-h) show_help=yes + ;; + --prefix=*) prefix="$optarg" + ;; + --interp-prefix=*) interp_prefix="$optarg" + ;; + --source-path=*) source_path="$optarg" + source_path_used="yes" + ;; + --cross-prefix=*) cross_prefix="$optarg" + ;; + --cc=*) cc="$optarg" + ;; + --host-cc=*) host_cc="$optarg" + ;; + --make=*) make="$optarg" + ;; + --install=*) install="$optarg" + ;; + --extra-cflags=*) CFLAGS="$optarg" + ;; + --extra-ldflags=*) LDFLAGS="$optarg" + ;; + --cpu=*) cpu="$optarg" + ;; + --target-list=*) target_list="$optarg" + ;; + --enable-gprof) gprof="yes" + ;; + --static) static="yes" + ;; + --disable-sdl) sdl="no" + ;; + --enable-coreaudio) coreaudio="yes" + ;; + --enable-alsa) alsa="yes" + ;; + --enable-dsound) dsound="yes" + ;; + --enable-fmod) fmod="yes" + ;; + --fmod-lib=*) fmod_lib="$optarg" + ;; + --fmod-inc=*) fmod_inc="$optarg" + ;; + --enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-" + ;; + --disable-slirp) slirp="no" + ;; + --enable-adlib) adlib="yes" + ;; + --disable-kqemu) kqemu="no" + ;; + --enable-profiler) profiler="yes" + ;; + --kernel-path=*) kernel_path="$optarg" + ;; + --enable-cocoa) cocoa="yes" ; coreaudio="yes" ; sdl="no" + ;; + --disable-gfx-check) check_gfx="no" + ;; + --disable-gcc-check) check_gcc="no" + ;; + --disable-system) softmmu="no" + ;; + --enable-system) softmmu="yes" + ;; + --disable-user) user="no" + ;; + --enable-user) user="yes" + ;; + esac +done + +# Checking for CFLAGS +if test -z "$CFLAGS"; then + CFLAGS="-O2" +fi + +if test x"$show_help" = x"yes" ; then +cat << EOF + +Usage: configure [options] +Options: [defaults in brackets after descriptions] + +EOF +echo "Standard options:" +echo " --help print this message" +echo " --prefix=PREFIX install in PREFIX [$prefix]" +echo " --interp-prefix=PREFIX where to find shared libraries, etc." +echo " use %M for cpu name [$interp_prefix]" +echo " --target-list=LIST set target list [$target_list]" +echo "" +echo "kqemu kernel acceleration support:" +echo " --disable-kqemu disable kqemu support" +echo " --kernel-path=PATH set the kernel path (configure probes it)" +echo "" +echo "Advanced options (experts only):" +echo " --source-path=PATH path of source code [$source_path]" +echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]" +echo " --cc=CC use C compiler CC [$cc]" +echo " --host-cc=CC use C compiler CC [$host_cc] for dyngen etc." +echo " --make=MAKE use specified make [$make]" +echo " --install=INSTALL use specified install [$install]" +echo " --static enable static build [$static]" +echo " --enable-cocoa enable COCOA (Mac OS X only)" +echo " --enable-mingw32 enable Win32 cross compilation with mingw32" +echo " --enable-adlib enable Adlib emulation" +echo " --enable-coreaudio enable Coreaudio audio driver" +echo " --enable-alsa enable ALSA audio driver" +echo " --enable-fmod enable FMOD audio driver" +echo " --enabled-dsound enable DirectSound audio driver" +echo " --enable-system enable all system emulation targets" +echo " --disable-system disable all system emulation targets" +echo " --enable-user enable all linux usermode emulation targets" +echo " --disable-user disable all linux usermode emulation targets" +echo " --fmod-lib path to FMOD library" +echo " --fmod-inc path to FMOD includes" +echo "" +echo "NOTE: The object files are build at the place where configure is launched" +exit 1 +fi + +cc="${cross_prefix}${cc}" +ar="${cross_prefix}${ar}" +strip="${cross_prefix}${strip}" + +if [ ! -x "`which $cc`" ] ; then + echo "Compiler $cc could not be found" + exit +fi + +if test "$mingw32" = "yes" ; then + linux="no" + EXESUF=".exe" + gdbstub="no" + oss="no" + if [ "$cpu" = "i386" ] ; then + kqemu="yes" + fi +fi + +# +# Solaris specific configure tool chain decisions +# +if test "$solaris" = "yes" ; then + # + # gcc for solaris 10/fcs in /usr/sfw/bin doesn't compile qemu correctly + # override the check with --disable-gcc-check + # + if test "$solarisrev" -eq 10 -a "$check_gcc" = "yes" ; then + solgcc=`which $cc` + if test "$solgcc" = "/usr/sfw/bin/gcc" ; then + echo "Solaris 10/FCS gcc in /usr/sfw/bin will not compiled qemu correctly." + echo "please get gcc-3.4.3 or later, from www.blastwave.org using pkg-get -i gcc3" + echo "or get the latest patch from SunSolve for gcc" + exit 1 + fi + fi + solinst=`which $install 2> /dev/null | /usr/bin/grep -v "no $install in"` + if test -z "$solinst" ; then + echo "Solaris install program not found. Use --install=/usr/ucb/install or" + echo "install fileutils from www.blastwave.org using pkg-get -i fileutils" + echo "to get ginstall which is used by default (which lives in /opt/csw/bin)" + exit 1 + fi + if test "$solinst" = "/usr/sbin/install" ; then + echo "Error: Solaris /usr/sbin/install is not an appropriate install program." + echo "try ginstall from the GNU fileutils available from www.blastwave.org" + echo "using pkg-get -i fileutils, or use --install=/usr/ucb/install" + exit 1 + fi + sol_ar=`which ar 2> /dev/null | /usr/bin/grep -v "no ar in"` + if test -z "$sol_ar" ; then + echo "Error: No path includes ar" + if test -f /usr/ccs/bin/ar ; then + echo "Add /usr/ccs/bin to your path and rerun configure" + fi + exit 1 + fi +fi + + +if test -z "$target_list" ; then +# these targets are portable + if [ "$softmmu" = "yes" ] ; then + target_list="i386-softmmu ppc-softmmu sparc-softmmu x86_64-softmmu mips-softmmu mipsel-softmmu arm-softmmu" + fi +# the following are Linux specific + if [ "$user" = "yes" ] ; then + target_list="i386-user arm-user armeb-user sparc-user ppc-user mips-user mipsel-user $target_list" + fi +# the i386-dm target + target_list="i386-dm" +else + target_list=`echo "$target_list" | sed -e 's/,/ /g'` +fi +if test -z "$target_list" ; then + echo "No targets enabled" + exit 1 +fi + +kqemu="no" + +if test -z "$cross_prefix" ; then + +# --- +# big/little endian test +cat > $TMPC << EOF +#include +int main(int argc, char ** argv){ + volatile uint32_t i=0x01234567; + return (*((uint8_t*)(&i))) == 0x67; +} +EOF + +if $cc -o $TMPE $TMPC 2>/dev/null ; then +$TMPE && bigendian="yes" +else +echo big/little test failed +fi + +else + +# if cross compiling, cannot launch a program, so make a static guess +if test "$cpu" = "powerpc" -o "$cpu" = "mips" -o "$cpu" = "s390" -o "$cpu" = "sparc" -o "$cpu" = "sparc64" -o "$cpu" = "m68k" -o "$cpu" = "armv4b"; then + bigendian="yes" +fi + +fi + +# host long bits test +hostlongbits="32" +if test "$cpu" = "sparc64" -o "$cpu" = "ia64" -o "$cpu" = "x86_64" -o "$cpu" = "alpha"; then + hostlongbits="64" +fi + +# check gcc options support +cat > $TMPC < /dev/null ; then + have_gcc3_options="yes" +fi + +# Check for gcc4, error if pre-gcc4 +if test "$check_gcc" = "yes" ; then + cat > $TMPC </dev/null ; then + echo "ERROR: \"$cc\" looks like gcc 4.x" + echo "QEMU is known to have problems when compiled with gcc 4.x" + echo "It is recommended that you use gcc 3.x to build QEMU" + echo "To use this compiler anyway, configure with --disable-gcc-check" + exit 1; + fi +fi + +########################################## +# SDL probe + +sdl_too_old=no + +if test -z "$sdl" ; then + +sdl_config="sdl-config" +sdl=no +sdl_static=no + +if test "$mingw32" = "yes" -a ! -z "$cross_prefix" ; then +# win32 cross compilation case + sdl_config="i386-mingw32msvc-sdl-config" + sdl=yes +else +# normal SDL probe +cat > $TMPC << EOF +#include +#undef main /* We don't want SDL to override our main() */ +int main( void ) { return SDL_Init (SDL_INIT_VIDEO); } +EOF + +if $cc -o $TMPE `$sdl_config --cflags 2> /dev/null` $TMPC `$sdl_config --libs 2> /dev/null` 2> /dev/null ; then +_sdlversion=`$sdl_config --version | sed 's/[^0-9]//g'` +if test "$_sdlversion" -lt 121 ; then +sdl_too_old=yes +else +sdl=yes +fi + +# static link with sdl ? +if test "$sdl" = "yes" ; then +aa="no" +`$sdl_config --static-libs | grep \\\-laa > /dev/null` && aa="yes" +sdl_static_libs=`$sdl_config --static-libs` +if [ "$aa" = "yes" ] ; then + sdl_static_libs="$sdl_static_libs `aalib-config --static-libs`" +fi + +if $cc -o $TMPE `$sdl_config --cflags 2> /dev/null` $TMPC $sdl_static_libs 2> /dev/null; then + sdl_static=yes +fi + +fi # static link + +fi # sdl compile test + +fi # cross compilation +fi # -z $sdl + +# Check if tools are available to build documentation. +if [ -x "`which texi2html`" ] && [ -x "`which pod2man`" ]; then + build_docs="yes" +fi + +if test "$mingw32" = "yes" ; then +if test -z "$prefix" ; then + prefix="/c/Program Files/Qemu" +fi +mandir="$prefix" +datadir="$prefix" +docdir="$prefix" +bindir="$prefix" +configdir="" +else +if test -z "$prefix" ; then + prefix="/usr/local" +fi +mandir="$prefix/share/man" +datadir="$prefix/share/xen/qemu" +docdir="$prefix/share/doc/qemu" +bindir="$prefix/$libdir/xen/bin" +configdir="/etc/xen" +fi + +echo "Install prefix $prefix" +echo "BIOS directory $datadir" +echo "binary directory $bindir" +if test "$mingw32" = "no" ; then +echo "Manual directory $mandir" +echo "ELF interp prefix $interp_prefix" +fi +echo "Source path $source_path" +echo "C compiler $cc" +echo "Host C compiler $host_cc" +echo "make $make" +echo "install $install" +echo "host CPU $cpu" +echo "host big endian $bigendian" +echo "target list $target_list" +echo "gprof enabled $gprof" +echo "profiler $profiler" +echo "static build $static" +if test "$darwin" = "yes" ; then + echo "Cocoa support $cocoa" +fi +echo "SDL support $sdl" +if test "$sdl" != "no" ; then + echo "SDL static link $sdl_static" +fi +echo "mingw32 support $mingw32" +echo "Adlib support $adlib" +echo "CoreAudio support $coreaudio" +echo "ALSA support $alsa" +echo "DSound support $dsound" +if test "$fmod" = "yes"; then + if test -z $fmod_lib || test -z $fmod_inc; then + echo + echo "Error: You must specify path to FMOD library and headers" + echo "Example: --fmod-inc=/path/include/fmod --fmod-lib=/path/lib/libfmod-3.74.so" + echo + exit 1 + fi + fmod_support=" (lib='$fmod_lib' include='$fmod_inc')" +else + fmod_support="" +fi +echo "FMOD support $fmod $fmod_support" +echo "kqemu support $kqemu" +echo "Documentation $build_docs" + +if test $sdl_too_old = "yes"; then +echo "-> Your SDL version is too old - please upgrade to have SDL support" +fi +#if test "$sdl_static" = "no"; then +# echo "WARNING: cannot compile statically with SDL - qemu-fast won't have a graphical output" +#fi +config_mak="config-host.mak" +config_h="config-host.h" + +#echo "Creating $config_mak and $config_h" + +echo "# Automatically generated by configure - do not modify" > $config_mak +echo "# Configured with: $0 $@" >> $config_mak +echo "/* Automatically generated by configure - do not modify */" > $config_h + +echo "prefix=$prefix" >> $config_mak +echo "bindir=$bindir" >> $config_mak +echo "mandir=$mandir" >> $config_mak +echo "datadir=$datadir" >> $config_mak +echo "docdir=$docdir" >> $config_mak +echo "configdir=$configdir" >> $config_mak +echo "LIBDIR=$libdir" >> $config_mak +echo "#define CONFIG_QEMU_SHAREDIR \"$datadir\"" >> $config_h +echo "MAKE=$make" >> $config_mak +echo "INSTALL=$install" >> $config_mak +echo "CC=$cc" >> $config_mak +if test "$have_gcc3_options" = "yes" ; then + echo "HAVE_GCC3_OPTIONS=yes" >> $config_mak +fi +echo "HOST_CC=$host_cc" >> $config_mak +echo "AR=$ar" >> $config_mak +echo "STRIP=$strip -s -R .comment -R .note" >> $config_mak +echo "CFLAGS=$CFLAGS" >> $config_mak +echo "LDFLAGS=$LDFLAGS" >> $config_mak +echo "EXESUF=$EXESUF" >> $config_mak +if test "$cpu" = "i386" ; then + echo "ARCH=i386" >> $config_mak + echo "#define HOST_I386 1" >> $config_h +elif test "$cpu" = "x86_64" ; then + echo "ARCH=x86_64" >> $config_mak + echo "#define HOST_X86_64 1" >> $config_h +elif test "$cpu" = "armv4b" ; then + echo "ARCH=arm" >> $config_mak + echo "#define HOST_ARM 1" >> $config_h +elif test "$cpu" = "armv4l" ; then + echo "ARCH=arm" >> $config_mak + echo "#define HOST_ARM 1" >> $config_h +elif test "$cpu" = "powerpc" ; then + echo "ARCH=ppc" >> $config_mak + echo "#define HOST_PPC 1" >> $config_h +elif test "$cpu" = "mips" ; then + echo "ARCH=mips" >> $config_mak + echo "#define HOST_MIPS 1" >> $config_h +elif test "$cpu" = "s390" ; then + echo "ARCH=s390" >> $config_mak + echo "#define HOST_S390 1" >> $config_h +elif test "$cpu" = "alpha" ; then + echo "ARCH=alpha" >> $config_mak + echo "#define HOST_ALPHA 1" >> $config_h +elif test "$cpu" = "sparc" ; then + echo "ARCH=sparc" >> $config_mak + echo "#define HOST_SPARC 1" >> $config_h +elif test "$cpu" = "sparc64" ; then + echo "ARCH=sparc64" >> $config_mak + echo "#define HOST_SPARC64 1" >> $config_h +elif test "$cpu" = "ia64" ; then + echo "ARCH=ia64" >> $config_mak + echo "#define HOST_IA64 1" >> $config_h +elif test "$cpu" = "m68k" ; then + echo "ARCH=m68k" >> $config_mak + echo "#define HOST_M68K 1" >> $config_h +else + echo "Unsupported CPU" + exit 1 +fi +if test "$bigendian" = "yes" ; then + echo "WORDS_BIGENDIAN=yes" >> $config_mak + echo "#define WORDS_BIGENDIAN 1" >> $config_h +fi +echo "#define HOST_LONG_BITS $hostlongbits" >> $config_h +if test "$mingw32" = "yes" ; then + echo "CONFIG_WIN32=yes" >> $config_mak + echo "#define CONFIG_WIN32 1" >> $config_h +elif test -f "/usr/include/byteswap.h" ; then + echo "#define HAVE_BYTESWAP_H 1" >> $config_h +fi +if test "$darwin" = "yes" ; then + echo "CONFIG_DARWIN=yes" >> $config_mak + echo "#define CONFIG_DARWIN 1" >> $config_h +fi +if test "$solaris" = "yes" ; then + echo "CONFIG_SOLARIS=yes" >> $config_mak + echo "#define HOST_SOLARIS $solarisrev" >> $config_h +fi +if test "$gdbstub" = "yes" ; then + echo "CONFIG_GDBSTUB=yes" >> $config_mak + echo "#define CONFIG_GDBSTUB 1" >> $config_h +fi +if test "$gprof" = "yes" ; then + echo "TARGET_GPROF=yes" >> $config_mak + echo "#define HAVE_GPROF 1" >> $config_h +fi +if test "$static" = "yes" ; then + echo "CONFIG_STATIC=yes" >> $config_mak + echo "#define CONFIG_STATIC 1" >> $config_h +fi +if test $profiler = "yes" ; then + echo "#define CONFIG_PROFILER 1" >> $config_h +fi +if test "$slirp" = "yes" ; then + echo "CONFIG_SLIRP=yes" >> $config_mak + echo "#define CONFIG_SLIRP 1" >> $config_h +fi +if test "$adlib" = "yes" ; then + echo "CONFIG_ADLIB=yes" >> $config_mak + echo "#define CONFIG_ADLIB 1" >> $config_h +fi +if test "$oss" = "yes" ; then + echo "CONFIG_OSS=yes" >> $config_mak + echo "#define CONFIG_OSS 1" >> $config_h +fi +if test "$coreaudio" = "yes" ; then + echo "CONFIG_COREAUDIO=yes" >> $config_mak + echo "#define CONFIG_COREAUDIO 1" >> $config_h +fi +if test "$alsa" = "yes" ; then + echo "CONFIG_ALSA=yes" >> $config_mak + echo "#define CONFIG_ALSA 1" >> $config_h +fi +if test "$dsound" = "yes" ; then + echo "CONFIG_DSOUND=yes" >> $config_mak + echo "#define CONFIG_DSOUND 1" >> $config_h +fi +if test "$fmod" = "yes" ; then + echo "CONFIG_FMOD=yes" >> $config_mak + echo "CONFIG_FMOD_LIB=$fmod_lib" >> $config_mak + echo "CONFIG_FMOD_INC=$fmod_inc" >> $config_mak + echo "#define CONFIG_FMOD 1" >> $config_h +fi +qemu_version=`head $source_path/VERSION` +echo "VERSION=$qemu_version" >>$config_mak +echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h + +echo "SRC_PATH=$source_path" >> $config_mak +if [ "$source_path_used" = "yes" ]; then + echo "VPATH=$source_path" >> $config_mak +fi +echo "TARGET_DIRS=$target_list" >> $config_mak +if [ "$build_docs" = "yes" ] ; then + echo "BUILD_DOCS=yes" >> $config_mak +fi + +# XXX: suppress that +if [ "$bsd" = "yes" ] ; then + echo "#define O_LARGEFILE 0" >> $config_h + echo "#define MAP_ANONYMOUS MAP_ANON" >> $config_h + echo "#define _BSD 1" >> $config_h +fi + +for target in $target_list; do +target_dir="$target" +config_mak=$target_dir/config.mak +config_h=$target_dir/config.h +target_cpu=`echo $target | cut -d '-' -f 1` +target_bigendian="no" +[ "$target_cpu" = "armeb" ] && target_bigendian=yes +[ "$target_cpu" = "sparc" ] && target_bigendian=yes +[ "$target_cpu" = "sparc64" ] && target_bigendian=yes +[ "$target_cpu" = "ppc" ] && target_bigendian=yes +[ "$target_cpu" = "ppc64" ] && target_bigendian=yes +[ "$target_cpu" = "mips" ] && target_bigendian=yes +target_softmmu="no" +if expr $target : '.*-softmmu' > /dev/null ; then + target_softmmu="yes" +fi +#for support 256M guest +target_softmmu="yes" +target_user_only="no" +if expr $target : '.*-user' > /dev/null ; then + target_user_only="yes" +fi + +if test "$target_user_only" = "no" -a "$check_gfx" = "yes" \ + -a "$sdl" = "no" -a "$cocoa" = "no" ; then + echo "ERROR: QEMU requires SDL or Cocoa for graphical output" + echo "To build QEMU without graphical output configure with --disable-gfx-check" + echo "Note that this will disable all output from the virtual graphics card." + exit 1; +fi + +#echo "Creating $config_mak, $config_h and $target_dir/Makefile" + +mkdir -p $target_dir +mkdir -p $target_dir/fpu +if test "$target" = "arm-user" -o "$target" = "armeb-user" ; then + mkdir -p $target_dir/nwfpe +fi +if test "$target_user_only" = "no" ; then + mkdir -p $target_dir/slirp +fi + +# +# don't use ln -sf as not all "ln -sf" over write the file/link +# +rm -f $target_dir/Makefile +ln -s ../Makefile.target $target_dir/Makefile + + +echo "# Automatically generated by configure - do not modify" > $config_mak +echo "/* Automatically generated by configure - do not modify */" > $config_h + + +echo "include ../config-host.mak" >> $config_mak +echo "#include \"../config-host.h\"" >> $config_h + +interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_cpu/g"` +echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix1\"" >> $config_h + +target_sub= +if expr $target : '.*-dm' > /dev/null ; then + target_sub=-dm +fi +echo "TARGET_SUB=${target_sub}" >> $config_mak + +if test "$target_cpu" = "i386" ; then + echo "TARGET_ARCH=i386" >> $config_mak + echo "#define TARGET_ARCH \"i386\"" >> $config_h + echo "#define TARGET_I386 1" >> $config_h + if test $kqemu = "yes" -a "$target_softmmu" = "yes" -a $cpu = "i386" ; then + echo "#define USE_KQEMU 1" >> $config_h + fi +elif test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" ; then + echo "TARGET_ARCH=arm" >> $config_mak + echo "#define TARGET_ARCH \"arm\"" >> $config_h + echo "#define TARGET_ARM 1" >> $config_h +elif test "$target_cpu" = "sparc" ; then + echo "TARGET_ARCH=sparc" >> $config_mak + echo "#define TARGET_ARCH \"sparc\"" >> $config_h + echo "#define TARGET_SPARC 1" >> $config_h +elif test "$target_cpu" = "sparc64" ; then + echo "TARGET_ARCH=sparc64" >> $config_mak + echo "#define TARGET_ARCH \"sparc64\"" >> $config_h + echo "#define TARGET_SPARC 1" >> $config_h + echo "#define TARGET_SPARC64 1" >> $config_h +elif test "$target_cpu" = "ppc" ; then + echo "TARGET_ARCH=ppc" >> $config_mak + echo "#define TARGET_ARCH \"ppc\"" >> $config_h + echo "#define TARGET_PPC 1" >> $config_h +elif test "$target_cpu" = "ppc64" ; then + echo "TARGET_ARCH=ppc64" >> $config_mak + echo "#define TARGET_ARCH \"ppc64\"" >> $config_h + echo "#define TARGET_PPC 1" >> $config_h + echo "#define TARGET_PPC64 1" >> $config_h +elif test "$target_cpu" = "x86_64" ; then + echo "TARGET_ARCH=x86_64" >> $config_mak + echo "#define TARGET_ARCH \"x86_64\"" >> $config_h + echo "#define TARGET_I386 1" >> $config_h + echo "#define TARGET_X86_64 1" >> $config_h + if test $kqemu = "yes" -a "$target_softmmu" = "yes" -a $cpu = "x86_64" ; then + echo "#define USE_KQEMU 1" >> $config_h + fi +elif test "$target_cpu" = "mips" -o "$target_cpu" = "mipsel" ; then + echo "TARGET_ARCH=mips" >> $config_mak + echo "#define TARGET_ARCH \"mips\"" >> $config_h + echo "#define TARGET_MIPS 1" >> $config_h +elif test "$target_cpu" = "sh4" ; then + echo "TARGET_ARCH=sh4" >> $config_mak + echo "#define TARGET_ARCH \"sh4\"" >> $config_h + echo "#define TARGET_SH4 1" >> $config_h +else + echo "Unsupported target CPU" + exit 1 +fi +if test "$target_bigendian" = "yes" ; then + echo "TARGET_WORDS_BIGENDIAN=yes" >> $config_mak + echo "#define TARGET_WORDS_BIGENDIAN 1" >> $config_h +fi +if test "$target_softmmu" = "yes" ; then + echo "CONFIG_SOFTMMU=yes" >> $config_mak + echo "#define CONFIG_SOFTMMU 1" >> $config_h +fi +if test "$target_user_only" = "yes" ; then + echo "CONFIG_USER_ONLY=yes" >> $config_mak + echo "#define CONFIG_USER_ONLY 1" >> $config_h +fi + +if expr $target : '.*-dm' > /dev/null ; then + echo "#define CONFIG_DM 1" >> $config_h +fi + +if test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" ; then + echo "CONFIG_SOFTFLOAT=yes" >> $config_mak + echo "#define CONFIG_SOFTFLOAT 1" >> $config_h +fi +# sdl defines + +if test "$target_user_only" = "no"; then + if test "$target_softmmu" = "no" -o "$static" = "yes"; then + sdl1=$sdl_static + else + sdl1=$sdl + fi + if test "$sdl1" = "yes" ; then + echo "#define CONFIG_SDL 1" >> $config_h + echo "CONFIG_SDL=yes" >> $config_mak + if test "$target_softmmu" = "no" -o "$static" = "yes"; then + echo "SDL_LIBS=$sdl_static_libs" >> $config_mak + else + echo "SDL_LIBS=`$sdl_config --libs`" >> $config_mak + fi + if [ "${aa}" = "yes" ] ; then + echo "SDL_CFLAGS=`$sdl_config --cflags` `aalib-config --cflags`" >> $config_mak + else + echo "SDL_CFLAGS=`$sdl_config --cflags`" >> $config_mak + fi + fi +fi + +if test "$cocoa" = "yes" ; then + echo "#define CONFIG_COCOA 1" >> $config_h + echo "CONFIG_COCOA=yes" >> $config_mak +fi + +done # for target in $targets + +# build tree in object directory if source path is different from current one +if test "$source_path_used" = "yes" ; then + DIRS="tests" + FILES="Makefile tests/Makefile" + for dir in $DIRS ; do + mkdir -p $dir + done + # remove the link and recreate it, as not all "ln -sf" overwrite the link + for f in $FILES ; do + rm -f $f + ln -s $source_path/$f $f + done +fi + +rm -f $TMPO $TMPC $TMPE $TMPS diff --git a/tools/ioemu/console.c b/tools/ioemu/console.c new file mode 100644 index 0000000000..a4f54d2e84 --- /dev/null +++ b/tools/ioemu/console.c @@ -0,0 +1,1008 @@ +/* + * QEMU graphical console + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG_CONSOLE +#define DEFAULT_BACKSCROLL 512 +#define MAX_CONSOLES 12 + +#define RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) +#define RGB(r, g, b) RGBA(r, g, b, 0xff) + +typedef struct TextAttributes { + uint8_t fgcol:4; + uint8_t bgcol:4; + uint8_t bold:1; + uint8_t uline:1; + uint8_t blink:1; + uint8_t invers:1; + uint8_t unvisible:1; +} TextAttributes; + +typedef struct TextCell { + uint8_t ch; + TextAttributes t_attrib; +} TextCell; + +#define MAX_ESC_PARAMS 3 + +enum TTYState { + TTY_STATE_NORM, + TTY_STATE_ESC, + TTY_STATE_CSI, +}; + +/* ??? This is mis-named. + It is used for both text and graphical consoles. */ +struct TextConsole { + int text_console; /* true if text console */ + DisplayState *ds; + /* Graphic console state. */ + vga_hw_update_ptr hw_update; + vga_hw_invalidate_ptr hw_invalidate; + vga_hw_screen_dump_ptr hw_screen_dump; + void *hw; + + int g_width, g_height; + int width; + int height; + int total_height; + int backscroll_height; + int x, y; + int y_displayed; + int y_base; + TextAttributes t_attrib_default; /* default text attributes */ + TextAttributes t_attrib; /* currently active text attributes */ + TextCell *cells; + + enum TTYState state; + int esc_params[MAX_ESC_PARAMS]; + int nb_esc_params; + + /* kbd read handler */ + IOReadHandler *fd_read; + void *fd_opaque; +}; + +static TextConsole *active_console; +static TextConsole *consoles[MAX_CONSOLES]; +static int nb_consoles = 0; + +void vga_hw_update(void) +{ + if (active_console->hw_update) + active_console->hw_update(active_console->hw); +} + +void vga_hw_invalidate(void) +{ + if (active_console->hw_invalidate) + active_console->hw_invalidate(active_console->hw); +} + +void vga_hw_screen_dump(const char *filename) +{ + /* There is currently no was of specifying which screen we want to dump, + so always dump the dirst one. */ + if (consoles[0]->hw_screen_dump) + consoles[0]->hw_screen_dump(consoles[0]->hw, filename); +} + +/* convert a RGBA color to a color index usable in graphic primitives */ +static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba) +{ + unsigned int r, g, b, color; + + switch(ds->depth) { +#if 0 + case 8: + r = (rgba >> 16) & 0xff; + g = (rgba >> 8) & 0xff; + b = (rgba) & 0xff; + color = (rgb_to_index[r] * 6 * 6) + + (rgb_to_index[g] * 6) + + (rgb_to_index[b]); + break; +#endif + case 15: + r = (rgba >> 16) & 0xff; + g = (rgba >> 8) & 0xff; + b = (rgba) & 0xff; + color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); + break; + case 16: + r = (rgba >> 16) & 0xff; + g = (rgba >> 8) & 0xff; + b = (rgba) & 0xff; + color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); + break; + case 32: + default: + color = rgba; + break; + } + return color; +} + +static void vga_fill_rect (DisplayState *ds, + int posx, int posy, int width, int height, uint32_t color) +{ + uint8_t *d, *d1; + int x, y, bpp; + + bpp = (ds->depth + 7) >> 3; + d1 = ds->data + + ds->linesize * posy + bpp * posx; + for (y = 0; y < height; y++) { + d = d1; + switch(bpp) { + case 1: + for (x = 0; x < width; x++) { + *((uint8_t *)d) = color; + d++; + } + break; + case 2: + for (x = 0; x < width; x++) { + *((uint16_t *)d) = color; + d += 2; + } + break; + case 4: + for (x = 0; x < width; x++) { + *((uint32_t *)d) = color; + d += 4; + } + break; + } + d1 += ds->linesize; + } +} + +/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */ +static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h) +{ + const uint8_t *s; + uint8_t *d; + int wb, y, bpp; + + bpp = (ds->depth + 7) >> 3; + wb = w * bpp; + if (yd <= ys) { + s = ds->data + + ds->linesize * ys + bpp * xs; + d = ds->data + + ds->linesize * yd + bpp * xd; + for (y = 0; y < h; y++) { + memmove(d, s, wb); + d += ds->linesize; + s += ds->linesize; + } + } else { + s = ds->data + + ds->linesize * (ys + h - 1) + bpp * xs; + d = ds->data + + ds->linesize * (yd + h - 1) + bpp * xd; + for (y = 0; y < h; y++) { + memmove(d, s, wb); + d -= ds->linesize; + s -= ds->linesize; + } + } +} + +/***********************************************************/ +/* basic char display */ + +#define FONT_HEIGHT 16 +#define FONT_WIDTH 8 + +#include "vgafont.h" + +#define cbswap_32(__x) \ +((uint32_t)( \ + (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) + +#ifdef WORDS_BIGENDIAN +#define PAT(x) x +#else +#define PAT(x) cbswap_32(x) +#endif + +static const uint32_t dmask16[16] = { + PAT(0x00000000), + PAT(0x000000ff), + PAT(0x0000ff00), + PAT(0x0000ffff), + PAT(0x00ff0000), + PAT(0x00ff00ff), + PAT(0x00ffff00), + PAT(0x00ffffff), + PAT(0xff000000), + PAT(0xff0000ff), + PAT(0xff00ff00), + PAT(0xff00ffff), + PAT(0xffff0000), + PAT(0xffff00ff), + PAT(0xffffff00), + PAT(0xffffffff), +}; + +static const uint32_t dmask4[4] = { + PAT(0x00000000), + PAT(0x0000ffff), + PAT(0xffff0000), + PAT(0xffffffff), +}; + +static uint32_t color_table[2][8]; + +enum color_names { + COLOR_BLACK = 0, + COLOR_RED = 1, + COLOR_GREEN = 2, + COLOR_YELLOW = 3, + COLOR_BLUE = 4, + COLOR_MAGENTA = 5, + COLOR_CYAN = 6, + COLOR_WHITE = 7 +}; + +static const uint32_t color_table_rgb[2][8] = { + { /* dark */ + RGB(0x00, 0x00, 0x00), /* black */ + RGB(0xaa, 0x00, 0x00), /* red */ + RGB(0x00, 0xaa, 0x00), /* green */ + RGB(0xaa, 0xaa, 0x00), /* yellow */ + RGB(0x00, 0x00, 0xaa), /* blue */ + RGB(0xaa, 0x00, 0xaa), /* magenta */ + RGB(0x00, 0xaa, 0xaa), /* cyan */ + RGB(0xaa, 0xaa, 0xaa), /* white */ + }, + { /* bright */ + RGB(0x00, 0x00, 0x00), /* black */ + RGB(0xff, 0x00, 0x00), /* red */ + RGB(0x00, 0xff, 0x00), /* green */ + RGB(0xff, 0xff, 0x00), /* yellow */ + RGB(0x00, 0x00, 0xff), /* blue */ + RGB(0xff, 0x00, 0xff), /* magenta */ + RGB(0x00, 0xff, 0xff), /* cyan */ + RGB(0xff, 0xff, 0xff), /* white */ + } +}; + +static inline unsigned int col_expand(DisplayState *ds, unsigned int col) +{ + switch(ds->depth) { + case 8: + col |= col << 8; + col |= col << 16; + break; + case 15: + case 16: + col |= col << 16; + break; + default: + break; + } + + return col; +} +#ifdef DEBUG_CONSOLE +static void console_print_text_attributes(TextAttributes *t_attrib, char ch) +{ + if (t_attrib->bold) { + printf("b"); + } else { + printf(" "); + } + if (t_attrib->uline) { + printf("u"); + } else { + printf(" "); + } + if (t_attrib->blink) { + printf("l"); + } else { + printf(" "); + } + if (t_attrib->invers) { + printf("i"); + } else { + printf(" "); + } + if (t_attrib->unvisible) { + printf("n"); + } else { + printf(" "); + } + + printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch); +} +#endif + +static void vga_putcharxy(DisplayState *ds, int x, int y, int ch, + TextAttributes *t_attrib) +{ + uint8_t *d; + const uint8_t *font_ptr; + unsigned int font_data, linesize, xorcol, bpp; + int i; + unsigned int fgcol, bgcol; + +#ifdef DEBUG_CONSOLE + printf("x: %2i y: %2i", x, y); + console_print_text_attributes(t_attrib, ch); +#endif + + if (t_attrib->invers) { + bgcol = color_table[t_attrib->bold][t_attrib->fgcol]; + fgcol = color_table[t_attrib->bold][t_attrib->bgcol]; + } else { + fgcol = color_table[t_attrib->bold][t_attrib->fgcol]; + bgcol = color_table[t_attrib->bold][t_attrib->bgcol]; + } + + bpp = (ds->depth + 7) >> 3; + d = ds->data + + ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH; + linesize = ds->linesize; + font_ptr = vgafont16 + FONT_HEIGHT * ch; + xorcol = bgcol ^ fgcol; + switch(ds->depth) { + case 8: + for(i = 0; i < FONT_HEIGHT; i++) { + font_data = *font_ptr++; + if (t_attrib->uline + && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) { + font_data = 0xFFFF; + } + ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol; + ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol; + d += linesize; + } + break; + case 16: + case 15: + for(i = 0; i < FONT_HEIGHT; i++) { + font_data = *font_ptr++; + if (t_attrib->uline + && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) { + font_data = 0xFFFF; + } + ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol; + ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol; + ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol; + ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol; + d += linesize; + } + break; + case 32: + for(i = 0; i < FONT_HEIGHT; i++) { + font_data = *font_ptr++; + if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) { + font_data = 0xFFFF; + } + ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol; + ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol; + d += linesize; + } + break; + } +} + +static void text_console_resize(TextConsole *s) +{ + TextCell *cells, *c, *c1; + int w1, x, y, last_width; + + last_width = s->width; + s->width = s->g_width / FONT_WIDTH; + s->height = s->g_height / FONT_HEIGHT; + + w1 = last_width; + if (s->width < w1) + w1 = s->width; + + cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell)); + for(y = 0; y < s->total_height; y++) { + c = &cells[y * s->width]; + if (w1 > 0) { + c1 = &s->cells[y * last_width]; + for(x = 0; x < w1; x++) { + *c++ = *c1++; + } + } + for(x = w1; x < s->width; x++) { + c->ch = ' '; + c->t_attrib = s->t_attrib_default; + c++; + } + } + qemu_free(s->cells); + s->cells = cells; +} + +static void update_xy(TextConsole *s, int x, int y) +{ + TextCell *c; + int y1, y2; + + if (s == active_console) { + y1 = (s->y_base + y) % s->total_height; + y2 = y1 - s->y_displayed; + if (y2 < 0) + y2 += s->total_height; + if (y2 < s->height) { + c = &s->cells[y1 * s->width + x]; + vga_putcharxy(s->ds, x, y2, c->ch, + &(c->t_attrib)); + dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT, + FONT_WIDTH, FONT_HEIGHT); + } + } +} + +static void console_show_cursor(TextConsole *s, int show) +{ + TextCell *c; + int y, y1; + + if (s == active_console) { + y1 = (s->y_base + s->y) % s->total_height; + y = y1 - s->y_displayed; + if (y < 0) + y += s->total_height; + if (y < s->height) { + c = &s->cells[y1 * s->width + s->x]; + if (show) { + TextAttributes t_attrib = s->t_attrib_default; + t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */ + vga_putcharxy(s->ds, s->x, y, c->ch, &t_attrib); + } else { + vga_putcharxy(s->ds, s->x, y, c->ch, + &(c->t_attrib)); + } + dpy_update(s->ds, s->x * FONT_WIDTH, y * FONT_HEIGHT, + FONT_WIDTH, FONT_HEIGHT); + } + } +} + +static void console_refresh(TextConsole *s) +{ + TextCell *c; + int x, y, y1; + + if (s != active_console) + return; + + vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height, + color_table[0][COLOR_BLACK]); + y1 = s->y_displayed; + for(y = 0; y < s->height; y++) { + c = s->cells + y1 * s->width; + for(x = 0; x < s->width; x++) { + vga_putcharxy(s->ds, x, y, c->ch, + &(c->t_attrib)); + c++; + } + if (++y1 == s->total_height) + y1 = 0; + } + dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height); + console_show_cursor(s, 1); +} + +static void console_scroll(int ydelta) +{ + TextConsole *s; + int i, y1; + + s = active_console; + if (!s || !s->text_console) + return; + + if (ydelta > 0) { + for(i = 0; i < ydelta; i++) { + if (s->y_displayed == s->y_base) + break; + if (++s->y_displayed == s->total_height) + s->y_displayed = 0; + } + } else { + ydelta = -ydelta; + i = s->backscroll_height; + if (i > s->total_height - s->height) + i = s->total_height - s->height; + y1 = s->y_base - i; + if (y1 < 0) + y1 += s->total_height; + for(i = 0; i < ydelta; i++) { + if (s->y_displayed == y1) + break; + if (--s->y_displayed < 0) + s->y_displayed = s->total_height - 1; + } + } + console_refresh(s); +} + +static void console_put_lf(TextConsole *s) +{ + TextCell *c; + int x, y1; + + s->x = 0; + s->y++; + if (s->y >= s->height) { + s->y = s->height - 1; + + if (s->y_displayed == s->y_base) { + if (++s->y_displayed == s->total_height) + s->y_displayed = 0; + } + if (++s->y_base == s->total_height) + s->y_base = 0; + if (s->backscroll_height < s->total_height) + s->backscroll_height++; + y1 = (s->y_base + s->height - 1) % s->total_height; + c = &s->cells[y1 * s->width]; + for(x = 0; x < s->width; x++) { + c->ch = ' '; + c->t_attrib = s->t_attrib_default; + c++; + } + if (s == active_console && s->y_displayed == s->y_base) { + vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0, + s->width * FONT_WIDTH, + (s->height - 1) * FONT_HEIGHT); + vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT, + s->width * FONT_WIDTH, FONT_HEIGHT, + color_table[0][s->t_attrib_default.bgcol]); + dpy_update(s->ds, 0, 0, + s->width * FONT_WIDTH, s->height * FONT_HEIGHT); + } + } +} + +/* Set console attributes depending on the current escape codes. + * NOTE: I know this code is not very efficient (checking every color for it + * self) but it is more readable and better maintainable. + */ +static void console_handle_escape(TextConsole *s) +{ + int i; + + if (s->nb_esc_params == 0) { /* ESC[m sets all attributes to default */ + s->t_attrib = s->t_attrib_default; + return; + } + for (i=0; inb_esc_params; i++) { + switch (s->esc_params[i]) { + case 0: /* reset all console attributes to default */ + s->t_attrib = s->t_attrib_default; + break; + case 1: + s->t_attrib.bold = 1; + break; + case 4: + s->t_attrib.uline = 1; + break; + case 5: + s->t_attrib.blink = 1; + break; + case 7: + s->t_attrib.invers = 1; + break; + case 8: + s->t_attrib.unvisible = 1; + break; + case 22: + s->t_attrib.bold = 0; + break; + case 24: + s->t_attrib.uline = 0; + break; + case 25: + s->t_attrib.blink = 0; + break; + case 27: + s->t_attrib.invers = 0; + break; + case 28: + s->t_attrib.unvisible = 0; + break; + /* set foreground color */ + case 30: + s->t_attrib.fgcol=COLOR_BLACK; + break; + case 31: + s->t_attrib.fgcol=COLOR_RED; + break; + case 32: + s->t_attrib.fgcol=COLOR_GREEN; + break; + case 33: + s->t_attrib.fgcol=COLOR_YELLOW; + break; + case 34: + s->t_attrib.fgcol=COLOR_BLUE; + break; + case 35: + s->t_attrib.fgcol=COLOR_MAGENTA; + break; + case 36: + s->t_attrib.fgcol=COLOR_CYAN; + break; + case 37: + s->t_attrib.fgcol=COLOR_WHITE; + break; + /* set background color */ + case 40: + s->t_attrib.bgcol=COLOR_BLACK; + break; + case 41: + s->t_attrib.bgcol=COLOR_RED; + break; + case 42: + s->t_attrib.bgcol=COLOR_GREEN; + break; + case 43: + s->t_attrib.bgcol=COLOR_YELLOW; + break; + case 44: + s->t_attrib.bgcol=COLOR_BLUE; + break; + case 45: + s->t_attrib.bgcol=COLOR_MAGENTA; + break; + case 46: + s->t_attrib.bgcol=COLOR_CYAN; + break; + case 47: + s->t_attrib.bgcol=COLOR_WHITE; + break; + } + } +} + +static void console_putchar(TextConsole *s, int ch) +{ + TextCell *c; + int y1, i, x; + + switch(s->state) { + case TTY_STATE_NORM: + switch(ch) { + case '\r': /* carriage return */ + s->x = 0; + break; + case '\n': /* newline */ + console_put_lf(s); + break; + case '\b': /* backspace */ + if(s->x > 0) s->x--; + y1 = (s->y_base + s->y) % s->total_height; + c = &s->cells[y1 * s->width + s->x]; + c->ch = ' '; + c->t_attrib = s->t_attrib; + update_xy(s, s->x, s->y); + break; + case '\t': /* tabspace */ + if (s->x + (8 - (s->x % 8)) > s->width) { + console_put_lf(s); + } else { + s->x = s->x + (8 - (s->x % 8)); + } + break; + case '\a': /* alert aka. bell */ + /* TODO: has to be implemented */ + break; + case 27: /* esc (introducing an escape sequence) */ + s->state = TTY_STATE_ESC; + break; + default: + y1 = (s->y_base + s->y) % s->total_height; + c = &s->cells[y1 * s->width + s->x]; + c->ch = ch; + c->t_attrib = s->t_attrib; + update_xy(s, s->x, s->y); + s->x++; + if (s->x >= s->width) + console_put_lf(s); + break; + } + break; + case TTY_STATE_ESC: /* check if it is a terminal escape sequence */ + if (ch == '[') { + for(i=0;iesc_params[i] = 0; + s->nb_esc_params = 0; + s->state = TTY_STATE_CSI; + } else { + s->state = TTY_STATE_NORM; + } + break; + case TTY_STATE_CSI: /* handle escape sequence parameters */ + if (ch >= '0' && ch <= '9') { + if (s->nb_esc_params < MAX_ESC_PARAMS) { + s->esc_params[s->nb_esc_params] = + s->esc_params[s->nb_esc_params] * 10 + ch - '0'; + } + } else { + s->nb_esc_params++; + if (ch == ';') + break; + s->state = TTY_STATE_NORM; + switch(ch) { + case 'D': + if (s->x > 0) + s->x--; + break; + case 'C': + if (s->x < (s->width - 1)) + s->x++; + break; + case 'K': + /* clear to eol */ + y1 = (s->y_base + s->y) % s->total_height; + for(x = s->x; x < s->width; x++) { + c = &s->cells[y1 * s->width + x]; + c->ch = ' '; + c->t_attrib = s->t_attrib_default; + c++; + update_xy(s, x, s->y); + } + break; + default: + break; + } + console_handle_escape(s); + break; + } + } +} + +void console_select(unsigned int index) +{ + TextConsole *s; + + if (index >= MAX_CONSOLES) + return; + s = consoles[index]; + if (s) { + active_console = s; + if (s->text_console) { + if (s->g_width != s->ds->width || + s->g_height != s->ds->height) { + s->g_width = s->ds->width; + s->g_height = s->ds->height; + text_console_resize(s); + } + console_refresh(s); + } else { + vga_hw_invalidate(); + } + } +} + +static int console_puts(CharDriverState *chr, const uint8_t *buf, int len) +{ + TextConsole *s = chr->opaque; + int i; + + console_show_cursor(s, 0); + for(i = 0; i < len; i++) { + console_putchar(s, buf[i]); + } + console_show_cursor(s, 1); + return len; +} + +static void console_chr_add_read_handler(CharDriverState *chr, + IOCanRWHandler *fd_can_read, + IOReadHandler *fd_read, void *opaque) +{ + TextConsole *s = chr->opaque; + s->fd_read = fd_read; + s->fd_opaque = opaque; +} + +static void console_send_event(CharDriverState *chr, int event) +{ + TextConsole *s = chr->opaque; + int i; + + if (event == CHR_EVENT_FOCUS) { + for(i = 0; i < nb_consoles; i++) { + if (consoles[i] == s) { + console_select(i); + break; + } + } + } +} + +/* called when an ascii key is pressed */ +void kbd_put_keysym(int keysym) +{ + TextConsole *s; + uint8_t buf[16], *q; + int c; + + s = active_console; + if (!s || !s->text_console) + return; + + switch(keysym) { + case QEMU_KEY_CTRL_UP: + console_scroll(-1); + break; + case QEMU_KEY_CTRL_DOWN: + console_scroll(1); + break; + case QEMU_KEY_CTRL_PAGEUP: + console_scroll(-10); + break; + case QEMU_KEY_CTRL_PAGEDOWN: + console_scroll(10); + break; + default: + if (s->fd_read) { + /* convert the QEMU keysym to VT100 key string */ + q = buf; + if (keysym >= 0xe100 && keysym <= 0xe11f) { + *q++ = '\033'; + *q++ = '['; + c = keysym - 0xe100; + if (c >= 10) + *q++ = '0' + (c / 10); + *q++ = '0' + (c % 10); + *q++ = '~'; + } else if (keysym >= 0xe120 && keysym <= 0xe17f) { + *q++ = '\033'; + *q++ = '['; + *q++ = keysym & 0xff; + } else { + *q++ = keysym; + } + s->fd_read(s->fd_opaque, buf, q - buf); + } + break; + } +} + +static TextConsole *new_console(DisplayState *ds, int text) +{ + TextConsole *s; + int i; + + if (nb_consoles >= MAX_CONSOLES) + return NULL; + s = qemu_mallocz(sizeof(TextConsole)); + if (!s) { + return NULL; + } + if (!active_console || (active_console->text_console && !text)) + active_console = s; + s->ds = ds; + s->text_console = text; + if (text) { + consoles[nb_consoles++] = s; + } else { + /* HACK: Put graphical consoles before text consoles. */ + for (i = nb_consoles; i > 0; i--) { + if (!consoles[i - 1]->text_console) + break; + consoles[i] = consoles[i - 1]; + } + consoles[i] = s; + } + return s; +} + +TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update, + vga_hw_invalidate_ptr invalidate, + vga_hw_screen_dump_ptr screen_dump, + void *opaque) +{ + TextConsole *s; + + s = new_console(ds, 0); + if (!s) + return NULL; + s->hw_update = update; + s->hw_invalidate = invalidate; + s->hw_screen_dump = screen_dump; + s->hw = opaque; + return s; +} + +int is_graphic_console(void) +{ + return !active_console->text_console; +} + +CharDriverState *text_console_init(DisplayState *ds) +{ + CharDriverState *chr; + TextConsole *s; + int i,j; + static int color_inited; + + chr = qemu_mallocz(sizeof(CharDriverState)); + if (!chr) + return NULL; + s = new_console(ds, 1); + if (!s) { + free(chr); + return NULL; + } + chr->opaque = s; + chr->chr_write = console_puts; + chr->chr_add_read_handler = console_chr_add_read_handler; + chr->chr_send_event = console_send_event; + + if (!color_inited) { + color_inited = 1; + for(j = 0; j < 2; j++) { + for(i = 0; i < 8; i++) { + color_table[j][i] = col_expand(s->ds, + vga_get_color(s->ds, color_table_rgb[j][i])); + } + } + } + s->y_displayed = 0; + s->y_base = 0; + s->total_height = DEFAULT_BACKSCROLL; + s->x = 0; + s->y = 0; + s->g_width = s->ds->width; + s->g_height = s->ds->height; + + /* Set text attribute defaults */ + s->t_attrib_default.bold = 0; + s->t_attrib_default.uline = 0; + s->t_attrib_default.blink = 0; + s->t_attrib_default.invers = 0; + s->t_attrib_default.unvisible = 0; + s->t_attrib_default.fgcol = COLOR_WHITE; + s->t_attrib_default.bgcol = COLOR_BLACK; + + /* set current text attributes to default */ + s->t_attrib = s->t_attrib_default; + text_console_resize(s); + + return chr; +} diff --git a/tools/ioemu/cpu-all.h b/tools/ioemu/cpu-all.h new file mode 100644 index 0000000000..b33b7b3679 --- /dev/null +++ b/tools/ioemu/cpu-all.h @@ -0,0 +1,944 @@ +/* + * defines common to all virtual CPUs + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef CPU_ALL_H +#define CPU_ALL_H + +#if defined(__arm__) || defined(__sparc__) +#define WORDS_ALIGNED +#endif + +/* some important defines: + * + * WORDS_ALIGNED : if defined, the host cpu can only make word aligned + * memory accesses. + * + * WORDS_BIGENDIAN : if defined, the host cpu is big endian and + * otherwise little endian. + * + * (TARGET_WORDS_ALIGNED : same for target cpu (not supported yet)) + * + * TARGET_WORDS_BIGENDIAN : same for target cpu + */ + +#include "bswap.h" + +#if defined(WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) +#define BSWAP_NEEDED +#endif + +#ifdef BSWAP_NEEDED + +static inline uint16_t tswap16(uint16_t s) +{ + return bswap16(s); +} + +static inline uint32_t tswap32(uint32_t s) +{ + return bswap32(s); +} + +static inline uint64_t tswap64(uint64_t s) +{ + return bswap64(s); +} + +static inline void tswap16s(uint16_t *s) +{ + *s = bswap16(*s); +} + +static inline void tswap32s(uint32_t *s) +{ + *s = bswap32(*s); +} + +static inline void tswap64s(uint64_t *s) +{ + *s = bswap64(*s); +} + +#else + +static inline uint16_t tswap16(uint16_t s) +{ + return s; +} + +static inline uint32_t tswap32(uint32_t s) +{ + return s; +} + +static inline uint64_t tswap64(uint64_t s) +{ + return s; +} + +static inline void tswap16s(uint16_t *s) +{ +} + +static inline void tswap32s(uint32_t *s) +{ +} + +static inline void tswap64s(uint64_t *s) +{ +} + +#endif + +#if TARGET_LONG_SIZE == 4 +#define tswapl(s) tswap32(s) +#define tswapls(s) tswap32s((uint32_t *)(s)) +#define bswaptls(s) bswap32s(s) +#else +#define tswapl(s) tswap64(s) +#define tswapls(s) tswap64s((uint64_t *)(s)) +#define bswaptls(s) bswap64s(s) +#endif + +/* NOTE: arm FPA is horrible as double 32 bit words are stored in big + endian ! */ +typedef union { + float64 d; +#if defined(WORDS_BIGENDIAN) \ + || (defined(__arm__) && !defined(__VFP_FP__) && !defined(CONFIG_SOFTFLOAT)) + struct { + uint32_t upper; + uint32_t lower; + } l; +#else + struct { + uint32_t lower; + uint32_t upper; + } l; +#endif + uint64_t ll; +} CPU_DoubleU; + +/* CPU memory access without any memory or io remapping */ + +/* + * the generic syntax for the memory accesses is: + * + * load: ld{type}{sign}{size}{endian}_{access_type}(ptr) + * + * store: st{type}{size}{endian}_{access_type}(ptr, val) + * + * type is: + * (empty): integer access + * f : float access + * + * sign is: + * (empty): for floats or 32 bit size + * u : unsigned + * s : signed + * + * size is: + * b: 8 bits + * w: 16 bits + * l: 32 bits + * q: 64 bits + * + * endian is: + * (empty): target cpu endianness or 8 bit access + * r : reversed target cpu endianness (not implemented yet) + * be : big endian (not implemented yet) + * le : little endian (not implemented yet) + * + * access_type is: + * raw : host memory access + * user : user mode access using soft MMU + * kernel : kernel mode access using soft MMU + */ +static inline int ldub_p(void *ptr) +{ + return *(uint8_t *)ptr; +} + +static inline int ldsb_p(void *ptr) +{ + return *(int8_t *)ptr; +} + +static inline void stb_p(void *ptr, int v) +{ + *(uint8_t *)ptr = v; +} + +/* NOTE: on arm, putting 2 in /proc/sys/debug/alignment so that the + kernel handles unaligned load/stores may give better results, but + it is a system wide setting : bad */ +#if defined(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED) + +/* conservative code for little endian unaligned accesses */ +static inline int lduw_le_p(void *ptr) +{ +#ifdef __powerpc__ + int val; + __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr)); + return val; +#else + uint8_t *p = ptr; + return p[0] | (p[1] << 8); +#endif +} + +static inline int ldsw_le_p(void *ptr) +{ +#ifdef __powerpc__ + int val; + __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr)); + return (int16_t)val; +#else + uint8_t *p = ptr; + return (int16_t)(p[0] | (p[1] << 8)); +#endif +} + +static inline int ldl_le_p(void *ptr) +{ +#ifdef __powerpc__ + int val; + __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (ptr)); + return val; +#else + uint8_t *p = ptr; + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); +#endif +} + +static inline uint64_t ldq_le_p(void *ptr) +{ + uint8_t *p = ptr; + uint32_t v1, v2; + v1 = ldl_le_p(p); + v2 = ldl_le_p(p + 4); + return v1 | ((uint64_t)v2 << 32); +} + +static inline void stw_le_p(void *ptr, int v) +{ +#ifdef __powerpc__ + __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(uint16_t *)ptr) : "r" (v), "r" (ptr)); +#else + uint8_t *p = ptr; + p[0] = v; + p[1] = v >> 8; +#endif +} + +static inline void stl_le_p(void *ptr, int v) +{ +#ifdef __powerpc__ + __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr)); +#else + uint8_t *p = ptr; + p[0] = v; + p[1] = v >> 8; + p[2] = v >> 16; + p[3] = v >> 24; +#endif +} + +static inline void stq_le_p(void *ptr, uint64_t v) +{ + uint8_t *p = ptr; + stl_le_p(p, (uint32_t)v); + stl_le_p(p + 4, v >> 32); +} + +/* float access */ + +static inline float32 ldfl_le_p(void *ptr) +{ + union { + float32 f; + uint32_t i; + } u; + u.i = ldl_le_p(ptr); + return u.f; +} + +static inline void stfl_le_p(void *ptr, float32 v) +{ + union { + float32 f; + uint32_t i; + } u; + u.f = v; + stl_le_p(ptr, u.i); +} + +static inline float64 ldfq_le_p(void *ptr) +{ + CPU_DoubleU u; + u.l.lower = ldl_le_p(ptr); + u.l.upper = ldl_le_p(ptr + 4); + return u.d; +} + +static inline void stfq_le_p(void *ptr, float64 v) +{ + CPU_DoubleU u; + u.d = v; + stl_le_p(ptr, u.l.lower); + stl_le_p(ptr + 4, u.l.upper); +} + +#else + +static inline int lduw_le_p(void *ptr) +{ + return *(uint16_t *)ptr; +} + +static inline int ldsw_le_p(void *ptr) +{ + return *(int16_t *)ptr; +} + +static inline int ldl_le_p(void *ptr) +{ + return *(uint32_t *)ptr; +} + +static inline uint64_t ldq_le_p(void *ptr) +{ + return *(uint64_t *)ptr; +} + +static inline void stw_le_p(void *ptr, int v) +{ + *(uint16_t *)ptr = v; +} + +static inline void stl_le_p(void *ptr, int v) +{ + *(uint32_t *)ptr = v; +} + +static inline void stq_le_p(void *ptr, uint64_t v) +{ + *(uint64_t *)ptr = v; +} + +/* float access */ + +static inline float32 ldfl_le_p(void *ptr) +{ + return *(float32 *)ptr; +} + +static inline float64 ldfq_le_p(void *ptr) +{ + return *(float64 *)ptr; +} + +static inline void stfl_le_p(void *ptr, float32 v) +{ + *(float32 *)ptr = v; +} + +static inline void stfq_le_p(void *ptr, float64 v) +{ + *(float64 *)ptr = v; +} +#endif + +#if !defined(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED) + +static inline int lduw_be_p(void *ptr) +{ +#if defined(__i386__) + int val; + asm volatile ("movzwl %1, %0\n" + "xchgb %b0, %h0\n" + : "=q" (val) + : "m" (*(uint16_t *)ptr)); + return val; +#else + uint8_t *b = (uint8_t *) ptr; + return ((b[0] << 8) | b[1]); +#endif +} + +static inline int ldsw_be_p(void *ptr) +{ +#if defined(__i386__) + int val; + asm volatile ("movzwl %1, %0\n" + "xchgb %b0, %h0\n" + : "=q" (val) + : "m" (*(uint16_t *)ptr)); + return (int16_t)val; +#else + uint8_t *b = (uint8_t *) ptr; + return (int16_t)((b[0] << 8) | b[1]); +#endif +} + +static inline int ldl_be_p(void *ptr) +{ +#if defined(__i386__) || defined(__x86_64__) + int val; + asm volatile ("movl %1, %0\n" + "bswap %0\n" + : "=r" (val) + : "m" (*(uint32_t *)ptr)); + return val; +#else + uint8_t *b = (uint8_t *) ptr; + return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; +#endif +} + +static inline uint64_t ldq_be_p(void *ptr) +{ + uint32_t a,b; + a = ldl_be_p(ptr); + b = ldl_be_p(ptr+4); + return (((uint64_t)a<<32)|b); +} + +static inline void stw_be_p(void *ptr, int v) +{ +#if defined(__i386__) + asm volatile ("xchgb %b0, %h0\n" + "movw %w0, %1\n" + : "=q" (v) + : "m" (*(uint16_t *)ptr), "0" (v)); +#else + uint8_t *d = (uint8_t *) ptr; + d[0] = v >> 8; + d[1] = v; +#endif +} + +static inline void stl_be_p(void *ptr, int v) +{ +#if defined(__i386__) || defined(__x86_64__) + asm volatile ("bswap %0\n" + "movl %0, %1\n" + : "=r" (v) + : "m" (*(uint32_t *)ptr), "0" (v)); +#else + uint8_t *d = (uint8_t *) ptr; + d[0] = v >> 24; + d[1] = v >> 16; + d[2] = v >> 8; + d[3] = v; +#endif +} + +static inline void stq_be_p(void *ptr, uint64_t v) +{ + stl_be_p(ptr, v >> 32); + stl_be_p(ptr + 4, v); +} + +/* float access */ + +static inline float32 ldfl_be_p(void *ptr) +{ + union { + float32 f; + uint32_t i; + } u; + u.i = ldl_be_p(ptr); + return u.f; +} + +static inline void stfl_be_p(void *ptr, float32 v) +{ + union { + float32 f; + uint32_t i; + } u; + u.f = v; + stl_be_p(ptr, u.i); +} + +static inline float64 ldfq_be_p(void *ptr) +{ + CPU_DoubleU u; + u.l.upper = ldl_be_p(ptr); + u.l.lower = ldl_be_p(ptr + 4); + return u.d; +} + +static inline void stfq_be_p(void *ptr, float64 v) +{ + CPU_DoubleU u; + u.d = v; + stl_be_p(ptr, u.l.upper); + stl_be_p(ptr + 4, u.l.lower); +} + +#else + +static inline int lduw_be_p(void *ptr) +{ + return *(uint16_t *)ptr; +} + +static inline int ldsw_be_p(void *ptr) +{ + return *(int16_t *)ptr; +} + +static inline int ldl_be_p(void *ptr) +{ + return *(uint32_t *)ptr; +} + +static inline uint64_t ldq_be_p(void *ptr) +{ + return *(uint64_t *)ptr; +} + +static inline void stw_be_p(void *ptr, int v) +{ + *(uint16_t *)ptr = v; +} + +static inline void stl_be_p(void *ptr, int v) +{ + *(uint32_t *)ptr = v; +} + +static inline void stq_be_p(void *ptr, uint64_t v) +{ + *(uint64_t *)ptr = v; +} + +/* float access */ + +static inline float32 ldfl_be_p(void *ptr) +{ + return *(float32 *)ptr; +} + +static inline float64 ldfq_be_p(void *ptr) +{ + return *(float64 *)ptr; +} + +static inline void stfl_be_p(void *ptr, float32 v) +{ + *(float32 *)ptr = v; +} + +static inline void stfq_be_p(void *ptr, float64 v) +{ + *(float64 *)ptr = v; +} + +#endif + +/* target CPU memory access functions */ +#if defined(TARGET_WORDS_BIGENDIAN) +#define lduw_p(p) lduw_be_p(p) +#define ldsw_p(p) ldsw_be_p(p) +#define ldl_p(p) ldl_be_p(p) +#define ldq_p(p) ldq_be_p(p) +#define ldfl_p(p) ldfl_be_p(p) +#define ldfq_p(p) ldfq_be_p(p) +#define stw_p(p, v) stw_be_p(p, v) +#define stl_p(p, v) stl_be_p(p, v) +#define stq_p(p, v) stq_be_p(p, v) +#define stfl_p(p, v) stfl_be_p(p, v) +#define stfq_p(p, v) stfq_be_p(p, v) +#else +#define lduw_p(p) lduw_le_p(p) +#define ldsw_p(p) ldsw_le_p(p) +#define ldl_p(p) ldl_le_p(p) +#define ldq_p(p) ldq_le_p(p) +#define ldfl_p(p) ldfl_le_p(p) +#define ldfq_p(p) ldfq_le_p(p) +#define stw_p(p, v) stw_le_p(p, v) +#define stl_p(p, v) stl_le_p(p, v) +#define stq_p(p, v) stq_le_p(p, v) +#define stfl_p(p, v) stfl_le_p(p, v) +#define stfq_p(p, v) stfq_le_p(p, v) +#endif + +/* MMU memory access macros */ + +#if defined(CONFIG_USER_ONLY) +/* On some host systems the guest address space is reserved on the host. + * This allows the guest address space to be offset to a convenient location. + */ +//#define GUEST_BASE 0x20000000 +#define GUEST_BASE 0 + +/* All direct uses of g2h and h2g need to go away for usermode softmmu. */ +#define g2h(x) ((void *)((unsigned long)(x) + GUEST_BASE)) +#define h2g(x) ((target_ulong)(x - GUEST_BASE)) + +#define saddr(x) g2h(x) +#define laddr(x) g2h(x) + +#else /* !CONFIG_USER_ONLY */ +/* NOTE: we use double casts if pointers and target_ulong have + different sizes */ +#define saddr(x) (uint8_t *)(long)(x) +#define laddr(x) (uint8_t *)(long)(x) +#endif + +#define ldub_raw(p) ldub_p(laddr((p))) +#define ldsb_raw(p) ldsb_p(laddr((p))) +#define lduw_raw(p) lduw_p(laddr((p))) +#define ldsw_raw(p) ldsw_p(laddr((p))) +#define ldl_raw(p) ldl_p(laddr((p))) +#define ldq_raw(p) ldq_p(laddr((p))) +#define ldfl_raw(p) ldfl_p(laddr((p))) +#define ldfq_raw(p) ldfq_p(laddr((p))) +#define stb_raw(p, v) stb_p(saddr((p)), v) +#define stw_raw(p, v) stw_p(saddr((p)), v) +#define stl_raw(p, v) stl_p(saddr((p)), v) +#define stq_raw(p, v) stq_p(saddr((p)), v) +#define stfl_raw(p, v) stfl_p(saddr((p)), v) +#define stfq_raw(p, v) stfq_p(saddr((p)), v) + + +#if defined(CONFIG_USER_ONLY) + +/* if user mode, no other memory access functions */ +#define ldub(p) ldub_raw(p) +#define ldsb(p) ldsb_raw(p) +#define lduw(p) lduw_raw(p) +#define ldsw(p) ldsw_raw(p) +#define ldl(p) ldl_raw(p) +#define ldq(p) ldq_raw(p) +#define ldfl(p) ldfl_raw(p) +#define ldfq(p) ldfq_raw(p) +#define stb(p, v) stb_raw(p, v) +#define stw(p, v) stw_raw(p, v) +#define stl(p, v) stl_raw(p, v) +#define stq(p, v) stq_raw(p, v) +#define stfl(p, v) stfl_raw(p, v) +#define stfq(p, v) stfq_raw(p, v) + +#define ldub_code(p) ldub_raw(p) +#define ldsb_code(p) ldsb_raw(p) +#define lduw_code(p) lduw_raw(p) +#define ldsw_code(p) ldsw_raw(p) +#define ldl_code(p) ldl_raw(p) + +#define ldub_kernel(p) ldub_raw(p) +#define ldsb_kernel(p) ldsb_raw(p) +#define lduw_kernel(p) lduw_raw(p) +#define ldsw_kernel(p) ldsw_raw(p) +#define ldl_kernel(p) ldl_raw(p) +#define ldfl_kernel(p) ldfl_raw(p) +#define ldfq_kernel(p) ldfq_raw(p) +#define stb_kernel(p, v) stb_raw(p, v) +#define stw_kernel(p, v) stw_raw(p, v) +#define stl_kernel(p, v) stl_raw(p, v) +#define stq_kernel(p, v) stq_raw(p, v) +#define stfl_kernel(p, v) stfl_raw(p, v) +#define stfq_kernel(p, vt) stfq_raw(p, v) + +#endif /* defined(CONFIG_USER_ONLY) */ + +/* page related stuff */ + +#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) +#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1) +#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK) + +/* ??? These should be the larger of unsigned long and target_ulong. */ +extern unsigned long qemu_real_host_page_size; +extern unsigned long qemu_host_page_bits; +extern unsigned long qemu_host_page_size; +extern unsigned long qemu_host_page_mask; + +#define HOST_PAGE_ALIGN(addr) (((addr) + qemu_host_page_size - 1) & qemu_host_page_mask) + +/* same as PROT_xxx */ +#define PAGE_READ 0x0001 +#define PAGE_WRITE 0x0002 +#define PAGE_EXEC 0x0004 +#define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC) +#define PAGE_VALID 0x0008 +/* original state of the write flag (used when tracking self-modifying + code */ +#define PAGE_WRITE_ORG 0x0010 + +void page_dump(FILE *f); +int page_get_flags(target_ulong address); +void page_set_flags(target_ulong start, target_ulong end, int flags); +void page_unprotect_range(target_ulong data, target_ulong data_size); + +#ifdef CONFIG_DM +#define SINGLE_CPU_DEFINES +#endif +#ifdef SINGLE_CPU_DEFINES + +#if defined(TARGET_I386) + +#define CPUState CPUX86State +#define cpu_init cpu_x86_init +#define cpu_exec cpu_x86_exec +#define cpu_gen_code cpu_x86_gen_code +#define cpu_signal_handler cpu_x86_signal_handler + +#elif defined(TARGET_ARM) + +#define CPUState CPUARMState +#define cpu_init cpu_arm_init +#define cpu_exec cpu_arm_exec +#define cpu_gen_code cpu_arm_gen_code +#define cpu_signal_handler cpu_arm_signal_handler + +#elif defined(TARGET_SPARC) + +#define CPUState CPUSPARCState +#define cpu_init cpu_sparc_init +#define cpu_exec cpu_sparc_exec +#define cpu_gen_code cpu_sparc_gen_code +#define cpu_signal_handler cpu_sparc_signal_handler + +#elif defined(TARGET_PPC) + +#define CPUState CPUPPCState +#define cpu_init cpu_ppc_init +#define cpu_exec cpu_ppc_exec +#define cpu_gen_code cpu_ppc_gen_code +#define cpu_signal_handler cpu_ppc_signal_handler + +#elif defined(TARGET_MIPS) +#define CPUState CPUMIPSState +#define cpu_init cpu_mips_init +#define cpu_exec cpu_mips_exec +#define cpu_gen_code cpu_mips_gen_code +#define cpu_signal_handler cpu_mips_signal_handler + +#elif defined(TARGET_SH4) +#define CPUState CPUSH4State +#define cpu_init cpu_sh4_init +#define cpu_exec cpu_sh4_exec +#define cpu_gen_code cpu_sh4_gen_code +#define cpu_signal_handler cpu_sh4_signal_handler + +#else + +#error unsupported target CPU + +#endif + +#else /* SINGLE_CPU_DEFINES */ + +#define CPUState CPUX86State +#define cpu_init cpu_x86_init +int main_loop(void); + +#endif /* SINGLE_CPU_DEFINES */ + +void cpu_dump_state(CPUState *env, FILE *f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...), + int flags); + +void cpu_abort(CPUState *env, const char *fmt, ...); +extern CPUState *first_cpu; +extern CPUState *cpu_single_env; +extern int code_copy_enabled; + +#define CPU_INTERRUPT_EXIT 0x01 /* wants exit from main loop */ +#define CPU_INTERRUPT_HARD 0x02 /* hardware interrupt pending */ +#define CPU_INTERRUPT_EXITTB 0x04 /* exit the current TB (use for x86 a20 case) */ +#define CPU_INTERRUPT_TIMER 0x08 /* internal timer exception pending */ +#define CPU_INTERRUPT_FIQ 0x10 /* Fast interrupt pending. */ +#define CPU_INTERRUPT_HALT 0x20 /* CPU halt wanted */ + +void cpu_interrupt(CPUState *s, int mask); +void cpu_reset_interrupt(CPUState *env, int mask); + +int cpu_breakpoint_insert(CPUState *env, target_ulong pc); +int cpu_breakpoint_remove(CPUState *env, target_ulong pc); +void cpu_single_step(CPUState *env, int enabled); +void cpu_reset(CPUState *s); + +/* Return the physical page corresponding to a virtual one. Use it + only for debugging because no protection checks are done. Return -1 + if no page found. */ +target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr); + +#define CPU_LOG_TB_OUT_ASM (1 << 0) +#define CPU_LOG_TB_IN_ASM (1 << 1) +#define CPU_LOG_TB_OP (1 << 2) +#define CPU_LOG_TB_OP_OPT (1 << 3) +#define CPU_LOG_INT (1 << 4) +#define CPU_LOG_EXEC (1 << 5) +#define CPU_LOG_PCALL (1 << 6) +#define CPU_LOG_IOPORT (1 << 7) +#define CPU_LOG_TB_CPU (1 << 8) + +/* define log items */ +typedef struct CPULogItem { + int mask; + const char *name; + const char *help; +} CPULogItem; + +extern CPULogItem cpu_log_items[]; + +void cpu_set_log(int log_flags); +void cpu_set_log_filename(const char *filename); +int cpu_str_to_log_mask(const char *str); + +/* IO ports API */ + +/* NOTE: as these functions may be even used when there is an isa + brige on non x86 targets, we always defined them */ +#ifndef NO_CPU_IO_DEFS +void cpu_outb(CPUState *env, int addr, int val); +void cpu_outw(CPUState *env, int addr, int val); +void cpu_outl(CPUState *env, int addr, int val); +int cpu_inb(CPUState *env, int addr); +int cpu_inw(CPUState *env, int addr); +int cpu_inl(CPUState *env, int addr); +#endif + +#if defined(__i386__) || defined(__x86_64__) +static __inline__ void atomic_set_bit(long nr, volatile void *addr) +{ + __asm__ __volatile__( + "lock ; bts %1,%0" + :"=m" (*(volatile long *)addr) + :"dIr" (nr)); +} +static __inline__ void atomic_clear_bit(long nr, volatile void *addr) +{ + __asm__ __volatile__( + "lock ; btr %1,%0" + :"=m" (*(volatile long *)addr) + :"dIr" (nr)); +} +#endif + +/* memory API */ + +extern uint64_t phys_ram_size; +extern int phys_ram_fd; +extern uint8_t *phys_ram_base; +extern uint8_t *phys_ram_dirty; + +/* physical memory access */ +#define TLB_INVALID_MASK (1 << 3) +#define IO_MEM_SHIFT 4 +#define IO_MEM_NB_ENTRIES (1 << (TARGET_PAGE_BITS - IO_MEM_SHIFT)) + +#define IO_MEM_RAM (0 << IO_MEM_SHIFT) /* hardcoded offset */ +#define IO_MEM_ROM (1 << IO_MEM_SHIFT) /* hardcoded offset */ +#define IO_MEM_UNASSIGNED (2 << IO_MEM_SHIFT) +#define IO_MEM_NOTDIRTY (4 << IO_MEM_SHIFT) /* used internally, never use directly */ + +typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value); +typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr); + +void cpu_register_physical_memory(target_phys_addr_t start_addr, + unsigned long size, + unsigned long phys_offset); +int cpu_register_io_memory(int io_index, + CPUReadMemoryFunc **mem_read, + CPUWriteMemoryFunc **mem_write, + void *opaque); +CPUWriteMemoryFunc **cpu_get_io_memory_write(int io_index); +CPUReadMemoryFunc **cpu_get_io_memory_read(int io_index); + +void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, + int len, int is_write); +static inline void cpu_physical_memory_read(target_phys_addr_t addr, + uint8_t *buf, int len) +{ + cpu_physical_memory_rw(addr, buf, len, 0); +} +static inline void cpu_physical_memory_write(target_phys_addr_t addr, + const uint8_t *buf, int len) +{ + cpu_physical_memory_rw(addr, (uint8_t *)buf, len, 1); +} +uint32_t ldub_phys(target_phys_addr_t addr); +uint32_t lduw_phys(target_phys_addr_t addr); +uint32_t ldl_phys(target_phys_addr_t addr); +uint64_t ldq_phys(target_phys_addr_t addr); +void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val); +void stb_phys(target_phys_addr_t addr, uint32_t val); +void stw_phys(target_phys_addr_t addr, uint32_t val); +void stl_phys(target_phys_addr_t addr, uint32_t val); +void stq_phys(target_phys_addr_t addr, uint64_t val); + +void cpu_physical_memory_write_rom(target_phys_addr_t addr, + const uint8_t *buf, int len); +int cpu_memory_rw_debug(CPUState *env, target_ulong addr, + uint8_t *buf, int len, int is_write); + +#define VGA_DIRTY_FLAG 0x01 +#define CODE_DIRTY_FLAG 0x02 + +/* read dirty bit (return 0 or 1) */ +static inline int cpu_physical_memory_is_dirty(ram_addr_t addr) +{ + return phys_ram_dirty[addr >> TARGET_PAGE_BITS] == 0xff; +} + +static inline int cpu_physical_memory_get_dirty(ram_addr_t addr, + int dirty_flags) +{ + return phys_ram_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags; +} + +static inline void cpu_physical_memory_set_dirty(ram_addr_t addr) +{ + phys_ram_dirty[addr >> TARGET_PAGE_BITS] = 0xff; +} + +void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, + int dirty_flags); +void cpu_tlb_update_dirty(CPUState *env); + +void dump_exec_info(FILE *f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); + +/* profiling */ +#ifdef CONFIG_PROFILER +static inline int64_t profile_getclock(void) +{ + int64_t val; + asm volatile ("rdtsc" : "=A" (val)); + return val; +} + +extern int64_t kqemu_time, kqemu_time_start; +extern int64_t qemu_time, qemu_time_start; +extern int64_t tlb_flush_time; +extern int64_t kqemu_exec_count; +extern int64_t dev_time; +extern int64_t kqemu_ret_int_count; +extern int64_t kqemu_ret_excp_count; +extern int64_t kqemu_ret_intr_count; + +#endif + +#endif /* CPU_ALL_H */ diff --git a/tools/ioemu/cpu-defs.h b/tools/ioemu/cpu-defs.h new file mode 100644 index 0000000000..665158a381 --- /dev/null +++ b/tools/ioemu/cpu-defs.h @@ -0,0 +1,125 @@ +/* + * common defines for all CPUs + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef CPU_DEFS_H +#define CPU_DEFS_H + +#include "config.h" +#include +#include +#include "osdep.h" + +#ifndef TARGET_LONG_BITS +#error TARGET_LONG_BITS must be defined before including this header +#endif + +#ifndef TARGET_PHYS_ADDR_BITS +#if TARGET_LONG_BITS >= HOST_LONG_BITS +#define TARGET_PHYS_ADDR_BITS TARGET_LONG_BITS +#else +#define TARGET_PHYS_ADDR_BITS HOST_LONG_BITS +#endif +#endif + +#define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8) + +/* target_ulong is the type of a virtual address */ +#if TARGET_LONG_SIZE == 4 +typedef int32_t target_long; +typedef uint32_t target_ulong; +#define TARGET_FMT_lx "%08x" +#elif TARGET_LONG_SIZE == 8 +typedef int64_t target_long; +typedef uint64_t target_ulong; +#define TARGET_FMT_lx "%016llx" +#else +#error TARGET_LONG_SIZE undefined +#endif + +/* target_phys_addr_t is the type of a physical address (its size can + be different from 'target_ulong'). We have sizeof(target_phys_addr) + = max(sizeof(unsigned long), + sizeof(size_of_target_physical_address)) because we must pass a + host pointer to memory operations in some cases */ + +#if TARGET_PHYS_ADDR_BITS == 32 +typedef uint32_t target_phys_addr_t; +#elif TARGET_PHYS_ADDR_BITS == 64 +typedef uint64_t target_phys_addr_t; +#else +#error TARGET_PHYS_ADDR_BITS undefined +#endif + +/* address in the RAM (different from a physical address) */ +typedef unsigned long ram_addr_t; + +#define HOST_LONG_SIZE (HOST_LONG_BITS / 8) + +#define EXCP_INTERRUPT 0x10000 /* async interruption */ +#define EXCP_HLT 0x10001 /* hlt instruction reached */ +#define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */ +#define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */ +#define MAX_BREAKPOINTS 32 + +#define TB_JMP_CACHE_BITS 12 +#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS) + +#define CPU_TLB_BITS 8 +#define CPU_TLB_SIZE (1 << CPU_TLB_BITS) + +typedef struct CPUTLBEntry { + /* bit 31 to TARGET_PAGE_BITS : virtual address + bit TARGET_PAGE_BITS-1..IO_MEM_SHIFT : if non zero, memory io + zone number + bit 3 : indicates that the entry is invalid + bit 2..0 : zero + */ + target_ulong addr_read; + target_ulong addr_write; + target_ulong addr_code; + /* addend to virtual address to get physical address */ + target_phys_addr_t addend; +} CPUTLBEntry; + +#define CPU_COMMON \ + struct TranslationBlock *current_tb; /* currently executing TB */ \ + /* soft mmu support */ \ + /* in order to avoid passing too many arguments to the memory \ + write helpers, we store some rarely used information in the CPU \ + context) */ \ + unsigned long mem_write_pc; /* host pc at which the memory was \ + written */ \ + target_ulong mem_write_vaddr; /* target virtual addr at which the \ + memory was written */ \ + /* 0 = kernel, 1 = user */ \ + CPUTLBEntry tlb_table[2][CPU_TLB_SIZE]; \ + struct TranslationBlock *tb_jmp_cache[TB_JMP_CACHE_SIZE]; \ + \ + /* from this point: preserved by CPU reset */ \ + /* ice debug support */ \ + target_ulong breakpoints[MAX_BREAKPOINTS]; \ + int nb_breakpoints; \ + int singlestep_enabled; \ + \ + void *next_cpu; /* next CPU sharing TB cache */ \ + int cpu_index; /* CPU index (informative) */ \ + /* user data */ \ + void *opaque; + +#endif diff --git a/tools/ioemu/cpu-exec.c b/tools/ioemu/cpu-exec.c new file mode 100644 index 0000000000..8a585c1066 --- /dev/null +++ b/tools/ioemu/cpu-exec.c @@ -0,0 +1,1480 @@ +/* + * i386 emulator main execution loop + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "config.h" +#include "exec.h" +#include "disas.h" + +#if !defined(CONFIG_SOFTMMU) +#undef EAX +#undef ECX +#undef EDX +#undef EBX +#undef ESP +#undef EBP +#undef ESI +#undef EDI +#undef EIP +#include +#include +#endif + +int tb_invalidated_flag; + +//#define DEBUG_EXEC +//#define DEBUG_SIGNAL + +#if defined(TARGET_ARM) || defined(TARGET_SPARC) +/* XXX: unify with i386 target */ +void cpu_loop_exit(void) +{ + longjmp(env->jmp_env, 1); +} +#endif +#ifndef TARGET_SPARC +#define reg_T2 +#endif + +/* exit the current TB from a signal handler. The host registers are + restored in a state compatible with the CPU emulator + */ +void cpu_resume_from_signal(CPUState *env1, void *puc) +{ +#if !defined(CONFIG_SOFTMMU) + struct ucontext *uc = puc; +#endif + + env = env1; + + /* XXX: restore cpu registers saved in host registers */ + +#if !defined(CONFIG_SOFTMMU) + if (puc) { + /* XXX: use siglongjmp ? */ + sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); + } +#endif + longjmp(env->jmp_env, 1); +} + + +static TranslationBlock *tb_find_slow(target_ulong pc, + target_ulong cs_base, + unsigned int flags) +{ + TranslationBlock *tb, **ptb1; + int code_gen_size; + unsigned int h; + target_ulong phys_pc, phys_page1, phys_page2, virt_page2; + uint8_t *tc_ptr; + + spin_lock(&tb_lock); + + tb_invalidated_flag = 0; + + regs_to_env(); /* XXX: do it just before cpu_gen_code() */ + + /* find translated block using physical mappings */ + phys_pc = get_phys_addr_code(env, pc); + phys_page1 = phys_pc & TARGET_PAGE_MASK; + phys_page2 = -1; + h = tb_phys_hash_func(phys_pc); + ptb1 = &tb_phys_hash[h]; + for(;;) { + tb = *ptb1; + if (!tb) + goto not_found; + if (tb->pc == pc && + tb->page_addr[0] == phys_page1 && + tb->cs_base == cs_base && + tb->flags == flags) { + /* check next page if needed */ + if (tb->page_addr[1] != -1) { + virt_page2 = (pc & TARGET_PAGE_MASK) + + TARGET_PAGE_SIZE; + phys_page2 = get_phys_addr_code(env, virt_page2); + if (tb->page_addr[1] == phys_page2) + goto found; + } else { + goto found; + } + } + ptb1 = &tb->phys_hash_next; + } + not_found: + /* if no translated code available, then translate it now */ + tb = tb_alloc(pc); + if (!tb) { + /* flush must be done */ + tb_flush(env); + /* cannot fail at this point */ + tb = tb_alloc(pc); + /* don't forget to invalidate previous TB info */ + tb_invalidated_flag = 1; + } + tc_ptr = code_gen_ptr; + tb->tc_ptr = tc_ptr; + tb->cs_base = cs_base; + tb->flags = flags; + cpu_gen_code(env, tb, CODE_GEN_MAX_SIZE, &code_gen_size); + code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); + + /* check next page if needed */ + virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; + phys_page2 = -1; + if ((pc & TARGET_PAGE_MASK) != virt_page2) { + phys_page2 = get_phys_addr_code(env, virt_page2); + } + tb_link_phys(tb, phys_pc, phys_page2); + + found: + /* we add the TB in the virtual pc hash table */ + env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb; + spin_unlock(&tb_lock); + return tb; +} + +static inline TranslationBlock *tb_find_fast(void) +{ + TranslationBlock *tb; + target_ulong cs_base, pc; + unsigned int flags; + + /* we record a subset of the CPU state. It will + always be the same before a given translated block + is executed. */ +#if defined(TARGET_I386) + flags = env->hflags; + flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK)); + cs_base = env->segs[R_CS].base; + pc = cs_base + env->eip; +#elif defined(TARGET_ARM) + flags = env->thumb | (env->vfp.vec_len << 1) + | (env->vfp.vec_stride << 4); + if ((env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) + flags |= (1 << 6); + if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) + flags |= (1 << 7); + cs_base = 0; + pc = env->regs[15]; +#elif defined(TARGET_SPARC) +#ifdef TARGET_SPARC64 + flags = (env->pstate << 2) | ((env->lsu & (DMMU_E | IMMU_E)) >> 2); +#else + flags = env->psrs | ((env->mmuregs[0] & (MMU_E | MMU_NF)) << 1); +#endif + cs_base = env->npc; + pc = env->pc; +#elif defined(TARGET_PPC) + flags = (msr_pr << MSR_PR) | (msr_fp << MSR_FP) | + (msr_se << MSR_SE) | (msr_le << MSR_LE); + cs_base = 0; + pc = env->nip; +#elif defined(TARGET_MIPS) + flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK); + cs_base = 0; + pc = env->PC; +#elif defined(TARGET_SH4) + flags = env->sr & (SR_MD | SR_RB); + cs_base = 0; /* XXXXX */ + pc = env->pc; +#else +#error unsupported CPU +#endif + tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)]; + if (__builtin_expect(!tb || tb->pc != pc || tb->cs_base != cs_base || + tb->flags != flags, 0)) { + tb = tb_find_slow(pc, cs_base, flags); + /* Note: we do it here to avoid a gcc bug on Mac OS X when + doing it in tb_find_slow */ + if (tb_invalidated_flag) { + /* as some TB could have been invalidated because + of memory exceptions while generating the code, we + must recompute the hash index here */ + T0 = 0; + } + } + return tb; +} + + +/* main execution loop */ + +int cpu_exec(CPUState *env1) +{ + int saved_T0, saved_T1; +#if defined(reg_T2) + int saved_T2; +#endif + CPUState *saved_env; +#if defined(TARGET_I386) +#ifdef reg_EAX + int saved_EAX; +#endif +#ifdef reg_ECX + int saved_ECX; +#endif +#ifdef reg_EDX + int saved_EDX; +#endif +#ifdef reg_EBX + int saved_EBX; +#endif +#ifdef reg_ESP + int saved_ESP; +#endif +#ifdef reg_EBP + int saved_EBP; +#endif +#ifdef reg_ESI + int saved_ESI; +#endif +#ifdef reg_EDI + int saved_EDI; +#endif +#elif defined(TARGET_SPARC) +#if defined(reg_REGWPTR) + uint32_t *saved_regwptr; +#endif +#endif +#ifdef __sparc__ + int saved_i7, tmp_T0; +#endif + int ret, interrupt_request; + void (*gen_func)(void); + TranslationBlock *tb; + uint8_t *tc_ptr; + +#if defined(TARGET_I386) + /* handle exit of HALTED state */ + if (env1->hflags & HF_HALTED_MASK) { + /* disable halt condition */ + if ((env1->interrupt_request & CPU_INTERRUPT_HARD) && + (env1->eflags & IF_MASK)) { + env1->hflags &= ~HF_HALTED_MASK; + } else { + return EXCP_HALTED; + } + } +#elif defined(TARGET_PPC) + if (env1->halted) { + if (env1->msr[MSR_EE] && + (env1->interrupt_request & + (CPU_INTERRUPT_HARD | CPU_INTERRUPT_TIMER))) { + env1->halted = 0; + } else { + return EXCP_HALTED; + } + } +#elif defined(TARGET_SPARC) + if (env1->halted) { + if ((env1->interrupt_request & CPU_INTERRUPT_HARD) && + (env1->psret != 0)) { + env1->halted = 0; + } else { + return EXCP_HALTED; + } + } +#elif defined(TARGET_ARM) + if (env1->halted) { + /* An interrupt wakes the CPU even if the I and F CPSR bits are + set. */ + if (env1->interrupt_request + & (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD)) { + env1->halted = 0; + } else { + return EXCP_HALTED; + } + } +#elif defined(TARGET_MIPS) + if (env1->halted) { + if (env1->interrupt_request & + (CPU_INTERRUPT_HARD | CPU_INTERRUPT_TIMER)) { + env1->halted = 0; + } else { + return EXCP_HALTED; + } + } +#endif + + cpu_single_env = env1; + + /* first we save global registers */ + saved_env = env; + env = env1; + saved_T0 = T0; + saved_T1 = T1; +#if defined(reg_T2) + saved_T2 = T2; +#endif +#ifdef __sparc__ + /* we also save i7 because longjmp may not restore it */ + asm volatile ("mov %%i7, %0" : "=r" (saved_i7)); +#endif + +#if defined(TARGET_I386) +#ifdef reg_EAX + saved_EAX = EAX; +#endif +#ifdef reg_ECX + saved_ECX = ECX; +#endif +#ifdef reg_EDX + saved_EDX = EDX; +#endif +#ifdef reg_EBX + saved_EBX = EBX; +#endif +#ifdef reg_ESP + saved_ESP = ESP; +#endif +#ifdef reg_EBP + saved_EBP = EBP; +#endif +#ifdef reg_ESI + saved_ESI = ESI; +#endif +#ifdef reg_EDI + saved_EDI = EDI; +#endif + + env_to_regs(); + /* put eflags in CPU temporary format */ + CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + DF = 1 - (2 * ((env->eflags >> 10) & 1)); + CC_OP = CC_OP_EFLAGS; + env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); +#elif defined(TARGET_ARM) +#elif defined(TARGET_SPARC) +#if defined(reg_REGWPTR) + saved_regwptr = REGWPTR; +#endif +#elif defined(TARGET_PPC) +#elif defined(TARGET_MIPS) +#elif defined(TARGET_SH4) + /* XXXXX */ +#else +#error unsupported target CPU +#endif + env->exception_index = -1; + + /* prepare setjmp context for exception handling */ + for(;;) { + if (setjmp(env->jmp_env) == 0) { + env->current_tb = NULL; + /* if an exception is pending, we execute it here */ + if (env->exception_index >= 0) { + if (env->exception_index >= EXCP_INTERRUPT) { + /* exit request from the cpu execution loop */ + ret = env->exception_index; + break; + } else if (env->user_mode_only) { + /* if user mode only, we simulate a fake exception + which will be hanlded outside the cpu execution + loop */ +#if defined(TARGET_I386) + do_interrupt_user(env->exception_index, + env->exception_is_int, + env->error_code, + env->exception_next_eip); +#endif + ret = env->exception_index; + break; + } else { +#if defined(TARGET_I386) + /* simulate a real cpu exception. On i386, it can + trigger new exceptions, but we do not handle + double or triple faults yet. */ + do_interrupt(env->exception_index, + env->exception_is_int, + env->error_code, + env->exception_next_eip, 0); +#elif defined(TARGET_PPC) + do_interrupt(env); +#elif defined(TARGET_MIPS) + do_interrupt(env); +#elif defined(TARGET_SPARC) + do_interrupt(env->exception_index); +#elif defined(TARGET_ARM) + do_interrupt(env); +#elif defined(TARGET_SH4) + do_interrupt(env); +#endif + } + env->exception_index = -1; + } +#ifdef USE_KQEMU + if (kqemu_is_ok(env) && env->interrupt_request == 0) { + int ret; + env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK); + ret = kqemu_cpu_exec(env); + /* put eflags in CPU temporary format */ + CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + DF = 1 - (2 * ((env->eflags >> 10) & 1)); + CC_OP = CC_OP_EFLAGS; + env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + if (ret == 1) { + /* exception */ + longjmp(env->jmp_env, 1); + } else if (ret == 2) { + /* softmmu execution needed */ + } else { + if (env->interrupt_request != 0) { + /* hardware interrupt will be executed just after */ + } else { + /* otherwise, we restart */ + longjmp(env->jmp_env, 1); + } + } + } +#endif + + T0 = 0; /* force lookup of first TB */ + for(;;) { +#ifdef __sparc__ + /* g1 can be modified by some libc? functions */ + tmp_T0 = T0; +#endif + interrupt_request = env->interrupt_request; + if (__builtin_expect(interrupt_request, 0)) { +#if defined(TARGET_I386) + /* if hardware interrupt pending, we execute it */ + if ((interrupt_request & CPU_INTERRUPT_HARD) && + (env->eflags & IF_MASK) && + !(env->hflags & HF_INHIBIT_IRQ_MASK)) { + int intno; + env->interrupt_request &= ~CPU_INTERRUPT_HARD; + intno = cpu_get_pic_interrupt(env); + if (loglevel & CPU_LOG_TB_IN_ASM) { + fprintf(logfile, "Servicing hardware INT=0x%02x\n", intno); + } + do_interrupt(intno, 0, 0, 0, 1); + /* ensure that no TB jump will be modified as + the program flow was changed */ +#ifdef __sparc__ + tmp_T0 = 0; +#else + T0 = 0; +#endif + } +#elif defined(TARGET_PPC) +#if 0 + if ((interrupt_request & CPU_INTERRUPT_RESET)) { + cpu_ppc_reset(env); + } +#endif + if (msr_ee != 0) { + if ((interrupt_request & CPU_INTERRUPT_HARD)) { + /* Raise it */ + env->exception_index = EXCP_EXTERNAL; + env->error_code = 0; + do_interrupt(env); + env->interrupt_request &= ~CPU_INTERRUPT_HARD; +#ifdef __sparc__ + tmp_T0 = 0; +#else + T0 = 0; +#endif + } else if ((interrupt_request & CPU_INTERRUPT_TIMER)) { + /* Raise it */ + env->exception_index = EXCP_DECR; + env->error_code = 0; + do_interrupt(env); + env->interrupt_request &= ~CPU_INTERRUPT_TIMER; +#ifdef __sparc__ + tmp_T0 = 0; +#else + T0 = 0; +#endif + } + } +#elif defined(TARGET_MIPS) + if ((interrupt_request & CPU_INTERRUPT_HARD) && + (env->CP0_Status & (1 << CP0St_IE)) && + (env->CP0_Status & env->CP0_Cause & 0x0000FF00) && + !(env->hflags & MIPS_HFLAG_EXL) && + !(env->hflags & MIPS_HFLAG_ERL) && + !(env->hflags & MIPS_HFLAG_DM)) { + /* Raise it */ + env->exception_index = EXCP_EXT_INTERRUPT; + env->error_code = 0; + do_interrupt(env); + env->interrupt_request &= ~CPU_INTERRUPT_HARD; +#ifdef __sparc__ + tmp_T0 = 0; +#else + T0 = 0; +#endif + } +#elif defined(TARGET_SPARC) + if ((interrupt_request & CPU_INTERRUPT_HARD) && + (env->psret != 0)) { + int pil = env->interrupt_index & 15; + int type = env->interrupt_index & 0xf0; + + if (((type == TT_EXTINT) && + (pil == 15 || pil > env->psrpil)) || + type != TT_EXTINT) { + env->interrupt_request &= ~CPU_INTERRUPT_HARD; + do_interrupt(env->interrupt_index); + env->interrupt_index = 0; +#ifdef __sparc__ + tmp_T0 = 0; +#else + T0 = 0; +#endif + } + } else if (interrupt_request & CPU_INTERRUPT_TIMER) { + //do_interrupt(0, 0, 0, 0, 0); + env->interrupt_request &= ~CPU_INTERRUPT_TIMER; + } else if (interrupt_request & CPU_INTERRUPT_HALT) { + env1->halted = 1; + return EXCP_HALTED; + } +#elif defined(TARGET_ARM) + if (interrupt_request & CPU_INTERRUPT_FIQ + && !(env->uncached_cpsr & CPSR_F)) { + env->exception_index = EXCP_FIQ; + do_interrupt(env); + } + if (interrupt_request & CPU_INTERRUPT_HARD + && !(env->uncached_cpsr & CPSR_I)) { + env->exception_index = EXCP_IRQ; + do_interrupt(env); + } +#elif defined(TARGET_SH4) + /* XXXXX */ +#endif + if (env->interrupt_request & CPU_INTERRUPT_EXITTB) { + env->interrupt_request &= ~CPU_INTERRUPT_EXITTB; + /* ensure that no TB jump will be modified as + the program flow was changed */ +#ifdef __sparc__ + tmp_T0 = 0; +#else + T0 = 0; +#endif + } + if (interrupt_request & CPU_INTERRUPT_EXIT) { + env->interrupt_request &= ~CPU_INTERRUPT_EXIT; + env->exception_index = EXCP_INTERRUPT; + cpu_loop_exit(); + } + } +#ifdef DEBUG_EXEC + if ((loglevel & CPU_LOG_TB_CPU)) { +#if defined(TARGET_I386) + /* restore flags in standard format */ +#ifdef reg_EAX + env->regs[R_EAX] = EAX; +#endif +#ifdef reg_EBX + env->regs[R_EBX] = EBX; +#endif +#ifdef reg_ECX + env->regs[R_ECX] = ECX; +#endif +#ifdef reg_EDX + env->regs[R_EDX] = EDX; +#endif +#ifdef reg_ESI + env->regs[R_ESI] = ESI; +#endif +#ifdef reg_EDI + env->regs[R_EDI] = EDI; +#endif +#ifdef reg_EBP + env->regs[R_EBP] = EBP; +#endif +#ifdef reg_ESP + env->regs[R_ESP] = ESP; +#endif + env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK); + cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP); + env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); +#elif defined(TARGET_ARM) + cpu_dump_state(env, logfile, fprintf, 0); +#elif defined(TARGET_SPARC) + REGWPTR = env->regbase + (env->cwp * 16); + env->regwptr = REGWPTR; + cpu_dump_state(env, logfile, fprintf, 0); +#elif defined(TARGET_PPC) + cpu_dump_state(env, logfile, fprintf, 0); +#elif defined(TARGET_MIPS) + cpu_dump_state(env, logfile, fprintf, 0); +#elif defined(TARGET_SH4) + cpu_dump_state(env, logfile, fprintf, 0); +#else +#error unsupported target CPU +#endif + } +#endif + tb = tb_find_fast(); +#ifdef DEBUG_EXEC + if ((loglevel & CPU_LOG_EXEC)) { + fprintf(logfile, "Trace 0x%08lx [" TARGET_FMT_lx "] %s\n", + (long)tb->tc_ptr, tb->pc, + lookup_symbol(tb->pc)); + } +#endif +#ifdef __sparc__ + T0 = tmp_T0; +#endif + /* see if we can patch the calling TB. When the TB + spans two pages, we cannot safely do a direct + jump. */ + { + if (T0 != 0 && +#if USE_KQEMU + (env->kqemu_enabled != 2) && +#endif + tb->page_addr[1] == -1 +#if defined(TARGET_I386) && defined(USE_CODE_COPY) + && (tb->cflags & CF_CODE_COPY) == + (((TranslationBlock *)(T0 & ~3))->cflags & CF_CODE_COPY) +#endif + ) { + spin_lock(&tb_lock); + tb_add_jump((TranslationBlock *)(long)(T0 & ~3), T0 & 3, tb); +#if defined(USE_CODE_COPY) + /* propagates the FP use info */ + ((TranslationBlock *)(T0 & ~3))->cflags |= + (tb->cflags & CF_FP_USED); +#endif + spin_unlock(&tb_lock); + } + } + tc_ptr = tb->tc_ptr; + env->current_tb = tb; + /* execute the generated code */ + gen_func = (void *)tc_ptr; +#if defined(__sparc__) + __asm__ __volatile__("call %0\n\t" + "mov %%o7,%%i0" + : /* no outputs */ + : "r" (gen_func) + : "i0", "i1", "i2", "i3", "i4", "i5"); +#elif defined(__arm__) + asm volatile ("mov pc, %0\n\t" + ".global exec_loop\n\t" + "exec_loop:\n\t" + : /* no outputs */ + : "r" (gen_func) + : "r1", "r2", "r3", "r8", "r9", "r10", "r12", "r14"); +#elif defined(TARGET_I386) && defined(USE_CODE_COPY) +{ + if (!(tb->cflags & CF_CODE_COPY)) { + if ((tb->cflags & CF_FP_USED) && env->native_fp_regs) { + save_native_fp_state(env); + } + gen_func(); + } else { + if ((tb->cflags & CF_FP_USED) && !env->native_fp_regs) { + restore_native_fp_state(env); + } + /* we work with native eflags */ + CC_SRC = cc_table[CC_OP].compute_all(); + CC_OP = CC_OP_EFLAGS; + asm(".globl exec_loop\n" + "\n" + "debug1:\n" + " pushl %%ebp\n" + " fs movl %10, %9\n" + " fs movl %11, %%eax\n" + " andl $0x400, %%eax\n" + " fs orl %8, %%eax\n" + " pushl %%eax\n" + " popf\n" + " fs movl %%esp, %12\n" + " fs movl %0, %%eax\n" + " fs movl %1, %%ecx\n" + " fs movl %2, %%edx\n" + " fs movl %3, %%ebx\n" + " fs movl %4, %%esp\n" + " fs movl %5, %%ebp\n" + " fs movl %6, %%esi\n" + " fs movl %7, %%edi\n" + " fs jmp *%9\n" + "exec_loop:\n" + " fs movl %%esp, %4\n" + " fs movl %12, %%esp\n" + " fs movl %%eax, %0\n" + " fs movl %%ecx, %1\n" + " fs movl %%edx, %2\n" + " fs movl %%ebx, %3\n" + " fs movl %%ebp, %5\n" + " fs movl %%esi, %6\n" + " fs movl %%edi, %7\n" + " pushf\n" + " popl %%eax\n" + " movl %%eax, %%ecx\n" + " andl $0x400, %%ecx\n" + " shrl $9, %%ecx\n" + " andl $0x8d5, %%eax\n" + " fs movl %%eax, %8\n" + " movl $1, %%eax\n" + " subl %%ecx, %%eax\n" + " fs movl %%eax, %11\n" + " fs movl %9, %%ebx\n" /* get T0 value */ + " popl %%ebp\n" + : + : "m" (*(uint8_t *)offsetof(CPUState, regs[0])), + "m" (*(uint8_t *)offsetof(CPUState, regs[1])), + "m" (*(uint8_t *)offsetof(CPUState, regs[2])), + "m" (*(uint8_t *)offsetof(CPUState, regs[3])), + "m" (*(uint8_t *)offsetof(CPUState, regs[4])), + "m" (*(uint8_t *)offsetof(CPUState, regs[5])), + "m" (*(uint8_t *)offsetof(CPUState, regs[6])), + "m" (*(uint8_t *)offsetof(CPUState, regs[7])), + "m" (*(uint8_t *)offsetof(CPUState, cc_src)), + "m" (*(uint8_t *)offsetof(CPUState, tmp0)), + "a" (gen_func), + "m" (*(uint8_t *)offsetof(CPUState, df)), + "m" (*(uint8_t *)offsetof(CPUState, saved_esp)) + : "%ecx", "%edx" + ); + } +} +#elif defined(__ia64) + struct fptr { + void *ip; + void *gp; + } fp; + + fp.ip = tc_ptr; + fp.gp = code_gen_buffer + 2 * (1 << 20); + (*(void (*)(void)) &fp)(); +#else + gen_func(); +#endif + env->current_tb = NULL; + /* reset soft MMU for next block (it can currently + only be set by a memory fault) */ +#if defined(TARGET_I386) && !defined(CONFIG_SOFTMMU) + if (env->hflags & HF_SOFTMMU_MASK) { + env->hflags &= ~HF_SOFTMMU_MASK; + /* do not allow linking to another block */ + T0 = 0; + } +#endif +#if defined(USE_KQEMU) +#define MIN_CYCLE_BEFORE_SWITCH (100 * 1000) + if (kqemu_is_ok(env) && + (cpu_get_time_fast() - env->last_io_time) >= MIN_CYCLE_BEFORE_SWITCH) { + cpu_loop_exit(); + } +#endif + } + } else { + env_to_regs(); + } + } /* for(;;) */ + + +#if defined(TARGET_I386) +#if defined(USE_CODE_COPY) + if (env->native_fp_regs) { + save_native_fp_state(env); + } +#endif + /* restore flags in standard format */ + env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK); + + /* restore global registers */ +#ifdef reg_EAX + EAX = saved_EAX; +#endif +#ifdef reg_ECX + ECX = saved_ECX; +#endif +#ifdef reg_EDX + EDX = saved_EDX; +#endif +#ifdef reg_EBX + EBX = saved_EBX; +#endif +#ifdef reg_ESP + ESP = saved_ESP; +#endif +#ifdef reg_EBP + EBP = saved_EBP; +#endif +#ifdef reg_ESI + ESI = saved_ESI; +#endif +#ifdef reg_EDI + EDI = saved_EDI; +#endif +#elif defined(TARGET_ARM) + /* XXX: Save/restore host fpu exception state?. */ +#elif defined(TARGET_SPARC) +#if defined(reg_REGWPTR) + REGWPTR = saved_regwptr; +#endif +#elif defined(TARGET_PPC) +#elif defined(TARGET_MIPS) +#elif defined(TARGET_SH4) + /* XXXXX */ +#else +#error unsupported target CPU +#endif +#ifdef __sparc__ + asm volatile ("mov %0, %%i7" : : "r" (saved_i7)); +#endif + T0 = saved_T0; + T1 = saved_T1; +#if defined(reg_T2) + T2 = saved_T2; +#endif + env = saved_env; + /* fail safe : never use cpu_single_env outside cpu_exec() */ + cpu_single_env = NULL; + return ret; +} + +/* must only be called from the generated code as an exception can be + generated */ +void tb_invalidate_page_range(target_ulong start, target_ulong end) +{ + /* XXX: cannot enable it yet because it yields to MMU exception + where NIP != read address on PowerPC */ +#if 0 + target_ulong phys_addr; + phys_addr = get_phys_addr_code(env, start); + tb_invalidate_phys_page_range(phys_addr, phys_addr + end - start, 0); +#endif +} + +#if defined(TARGET_I386) && defined(CONFIG_USER_ONLY) + +void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) +{ + CPUX86State *saved_env; + + saved_env = env; + env = s; + if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) { + selector &= 0xffff; + cpu_x86_load_seg_cache(env, seg_reg, selector, + (selector << 4), 0xffff, 0); + } else { + load_seg(seg_reg, selector); + } + env = saved_env; +} + +void cpu_x86_fsave(CPUX86State *s, uint8_t *ptr, int data32) +{ + CPUX86State *saved_env; + + saved_env = env; + env = s; + + helper_fsave((target_ulong)ptr, data32); + + env = saved_env; +} + +void cpu_x86_frstor(CPUX86State *s, uint8_t *ptr, int data32) +{ + CPUX86State *saved_env; + + saved_env = env; + env = s; + + helper_frstor((target_ulong)ptr, data32); + + env = saved_env; +} + +#endif /* TARGET_I386 */ + +#if !defined(CONFIG_SOFTMMU) + +#if defined(TARGET_I386) + +/* 'pc' is the host PC at which the exception was raised. 'address' is + the effective address of the memory exception. 'is_write' is 1 if a + write caused the exception and otherwise 0'. 'old_set' is the + signal set which should be restored */ +static inline int handle_cpu_signal(unsigned long pc, unsigned long address, + int is_write, sigset_t *old_set, + void *puc) +{ + TranslationBlock *tb; + int ret; + + if (cpu_single_env) + env = cpu_single_env; /* XXX: find a correct solution for multithread */ +#if defined(DEBUG_SIGNAL) + qemu_printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", + pc, address, is_write, *(unsigned long *)old_set); +#endif + /* XXX: locking issue */ + if (is_write && page_unprotect(h2g(address), pc, puc)) { + return 1; + } + + /* see if it is an MMU fault */ + ret = cpu_x86_handle_mmu_fault(env, address, is_write, + ((env->hflags & HF_CPL_MASK) == 3), 0); + if (ret < 0) + return 0; /* not an MMU fault */ + if (ret == 0) + return 1; /* the MMU fault was handled without causing real CPU fault */ + /* now we have a real cpu fault */ + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, puc); + } + if (ret == 1) { +#if 0 + printf("PF exception: EIP=0x%08x CR2=0x%08x error=0x%x\n", + env->eip, env->cr[2], env->error_code); +#endif + /* we restore the process signal mask as the sigreturn should + do it (XXX: use sigsetjmp) */ + sigprocmask(SIG_SETMASK, old_set, NULL); + raise_exception_err(env->exception_index, env->error_code); + } else { + /* activate soft MMU for this block */ + env->hflags |= HF_SOFTMMU_MASK; + cpu_resume_from_signal(env, puc); + } + /* never comes here */ + return 1; +} + +#elif defined(TARGET_ARM) +static inline int handle_cpu_signal(unsigned long pc, unsigned long address, + int is_write, sigset_t *old_set, + void *puc) +{ + TranslationBlock *tb; + int ret; + + if (cpu_single_env) + env = cpu_single_env; /* XXX: find a correct solution for multithread */ +#if defined(DEBUG_SIGNAL) + printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", + pc, address, is_write, *(unsigned long *)old_set); +#endif + /* XXX: locking issue */ + if (is_write && page_unprotect(h2g(address), pc, puc)) { + return 1; + } + /* see if it is an MMU fault */ + ret = cpu_arm_handle_mmu_fault(env, address, is_write, 1, 0); + if (ret < 0) + return 0; /* not an MMU fault */ + if (ret == 0) + return 1; /* the MMU fault was handled without causing real CPU fault */ + /* now we have a real cpu fault */ + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, puc); + } + /* we restore the process signal mask as the sigreturn should + do it (XXX: use sigsetjmp) */ + sigprocmask(SIG_SETMASK, old_set, NULL); + cpu_loop_exit(); +} +#elif defined(TARGET_SPARC) +static inline int handle_cpu_signal(unsigned long pc, unsigned long address, + int is_write, sigset_t *old_set, + void *puc) +{ + TranslationBlock *tb; + int ret; + + if (cpu_single_env) + env = cpu_single_env; /* XXX: find a correct solution for multithread */ +#if defined(DEBUG_SIGNAL) + printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", + pc, address, is_write, *(unsigned long *)old_set); +#endif + /* XXX: locking issue */ + if (is_write && page_unprotect(h2g(address), pc, puc)) { + return 1; + } + /* see if it is an MMU fault */ + ret = cpu_sparc_handle_mmu_fault(env, address, is_write, 1, 0); + if (ret < 0) + return 0; /* not an MMU fault */ + if (ret == 0) + return 1; /* the MMU fault was handled without causing real CPU fault */ + /* now we have a real cpu fault */ + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, puc); + } + /* we restore the process signal mask as the sigreturn should + do it (XXX: use sigsetjmp) */ + sigprocmask(SIG_SETMASK, old_set, NULL); + cpu_loop_exit(); +} +#elif defined (TARGET_PPC) +static inline int handle_cpu_signal(unsigned long pc, unsigned long address, + int is_write, sigset_t *old_set, + void *puc) +{ + TranslationBlock *tb; + int ret; + + if (cpu_single_env) + env = cpu_single_env; /* XXX: find a correct solution for multithread */ +#if defined(DEBUG_SIGNAL) + printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", + pc, address, is_write, *(unsigned long *)old_set); +#endif + /* XXX: locking issue */ + if (is_write && page_unprotect(h2g(address), pc, puc)) { + return 1; + } + + /* see if it is an MMU fault */ + ret = cpu_ppc_handle_mmu_fault(env, address, is_write, msr_pr, 0); + if (ret < 0) + return 0; /* not an MMU fault */ + if (ret == 0) + return 1; /* the MMU fault was handled without causing real CPU fault */ + + /* now we have a real cpu fault */ + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, puc); + } + if (ret == 1) { +#if 0 + printf("PF exception: NIP=0x%08x error=0x%x %p\n", + env->nip, env->error_code, tb); +#endif + /* we restore the process signal mask as the sigreturn should + do it (XXX: use sigsetjmp) */ + sigprocmask(SIG_SETMASK, old_set, NULL); + do_raise_exception_err(env->exception_index, env->error_code); + } else { + /* activate soft MMU for this block */ + cpu_resume_from_signal(env, puc); + } + /* never comes here */ + return 1; +} + +#elif defined (TARGET_MIPS) +static inline int handle_cpu_signal(unsigned long pc, unsigned long address, + int is_write, sigset_t *old_set, + void *puc) +{ + TranslationBlock *tb; + int ret; + + if (cpu_single_env) + env = cpu_single_env; /* XXX: find a correct solution for multithread */ +#if defined(DEBUG_SIGNAL) + printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", + pc, address, is_write, *(unsigned long *)old_set); +#endif + /* XXX: locking issue */ + if (is_write && page_unprotect(h2g(address), pc, puc)) { + return 1; + } + + /* see if it is an MMU fault */ + ret = cpu_mips_handle_mmu_fault(env, address, is_write, 1, 0); + if (ret < 0) + return 0; /* not an MMU fault */ + if (ret == 0) + return 1; /* the MMU fault was handled without causing real CPU fault */ + + /* now we have a real cpu fault */ + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, puc); + } + if (ret == 1) { +#if 0 + printf("PF exception: NIP=0x%08x error=0x%x %p\n", + env->nip, env->error_code, tb); +#endif + /* we restore the process signal mask as the sigreturn should + do it (XXX: use sigsetjmp) */ + sigprocmask(SIG_SETMASK, old_set, NULL); + do_raise_exception_err(env->exception_index, env->error_code); + } else { + /* activate soft MMU for this block */ + cpu_resume_from_signal(env, puc); + } + /* never comes here */ + return 1; +} + +#elif defined (TARGET_SH4) +static inline int handle_cpu_signal(unsigned long pc, unsigned long address, + int is_write, sigset_t *old_set, + void *puc) +{ + TranslationBlock *tb; + int ret; + + if (cpu_single_env) + env = cpu_single_env; /* XXX: find a correct solution for multithread */ +#if defined(DEBUG_SIGNAL) + printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", + pc, address, is_write, *(unsigned long *)old_set); +#endif + /* XXX: locking issue */ + if (is_write && page_unprotect(h2g(address), pc, puc)) { + return 1; + } + + /* see if it is an MMU fault */ + ret = cpu_sh4_handle_mmu_fault(env, address, is_write, 1, 0); + if (ret < 0) + return 0; /* not an MMU fault */ + if (ret == 0) + return 1; /* the MMU fault was handled without causing real CPU fault */ + + /* now we have a real cpu fault */ + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, puc); + } + if (ret == 1) { +#if 0 + printf("PF exception: NIP=0x%08x error=0x%x %p\n", + env->nip, env->error_code, tb); +#endif + /* we restore the process signal mask as the sigreturn should + do it (XXX: use sigsetjmp) */ + sigprocmask(SIG_SETMASK, old_set, NULL); + // do_raise_exception_err(env->exception_index, env->error_code); + } else { + /* activate soft MMU for this block */ + cpu_resume_from_signal(env, puc); + } + /* never comes here */ + return 1; +} +#else +#error unsupported target CPU +#endif + +#if defined(__i386__) + +#if defined(USE_CODE_COPY) +static void cpu_send_trap(unsigned long pc, int trap, + struct ucontext *uc) +{ + TranslationBlock *tb; + + if (cpu_single_env) + env = cpu_single_env; /* XXX: find a correct solution for multithread */ + /* now we have a real cpu fault */ + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, uc); + } + sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); + raise_exception_err(trap, env->error_code); +} +#endif + +int cpu_signal_handler(int host_signum, struct siginfo *info, + void *puc) +{ + struct ucontext *uc = puc; + unsigned long pc; + int trapno; + +#ifndef REG_EIP +/* for glibc 2.1 */ +#define REG_EIP EIP +#define REG_ERR ERR +#define REG_TRAPNO TRAPNO +#endif + pc = uc->uc_mcontext.gregs[REG_EIP]; + trapno = uc->uc_mcontext.gregs[REG_TRAPNO]; +#if defined(TARGET_I386) && defined(USE_CODE_COPY) + if (trapno == 0x00 || trapno == 0x05) { + /* send division by zero or bound exception */ + cpu_send_trap(pc, trapno, uc); + return 1; + } else +#endif + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + trapno == 0xe ? + (uc->uc_mcontext.gregs[REG_ERR] >> 1) & 1 : 0, + &uc->uc_sigmask, puc); +} + +#elif defined(__x86_64__) + +int cpu_signal_handler(int host_signum, struct siginfo *info, + void *puc) +{ + struct ucontext *uc = puc; + unsigned long pc; + + pc = uc->uc_mcontext.gregs[REG_RIP]; + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe ? + (uc->uc_mcontext.gregs[REG_ERR] >> 1) & 1 : 0, + &uc->uc_sigmask, puc); +} + +#elif defined(__powerpc__) + +/*********************************************************************** + * signal context platform-specific definitions + * From Wine + */ +#ifdef linux +/* All Registers access - only for local access */ +# define REG_sig(reg_name, context) ((context)->uc_mcontext.regs->reg_name) +/* Gpr Registers access */ +# define GPR_sig(reg_num, context) REG_sig(gpr[reg_num], context) +# define IAR_sig(context) REG_sig(nip, context) /* Program counter */ +# define MSR_sig(context) REG_sig(msr, context) /* Machine State Register (Supervisor) */ +# define CTR_sig(context) REG_sig(ctr, context) /* Count register */ +# define XER_sig(context) REG_sig(xer, context) /* User's integer exception register */ +# define LR_sig(context) REG_sig(link, context) /* Link register */ +# define CR_sig(context) REG_sig(ccr, context) /* Condition register */ +/* Float Registers access */ +# define FLOAT_sig(reg_num, context) (((double*)((char*)((context)->uc_mcontext.regs+48*4)))[reg_num]) +# define FPSCR_sig(context) (*(int*)((char*)((context)->uc_mcontext.regs+(48+32*2)*4))) +/* Exception Registers access */ +# define DAR_sig(context) REG_sig(dar, context) +# define DSISR_sig(context) REG_sig(dsisr, context) +# define TRAP_sig(context) REG_sig(trap, context) +#endif /* linux */ + +#ifdef __APPLE__ +# include +typedef struct ucontext SIGCONTEXT; +/* All Registers access - only for local access */ +# define REG_sig(reg_name, context) ((context)->uc_mcontext->ss.reg_name) +# define FLOATREG_sig(reg_name, context) ((context)->uc_mcontext->fs.reg_name) +# define EXCEPREG_sig(reg_name, context) ((context)->uc_mcontext->es.reg_name) +# define VECREG_sig(reg_name, context) ((context)->uc_mcontext->vs.reg_name) +/* Gpr Registers access */ +# define GPR_sig(reg_num, context) REG_sig(r##reg_num, context) +# define IAR_sig(context) REG_sig(srr0, context) /* Program counter */ +# define MSR_sig(context) REG_sig(srr1, context) /* Machine State Register (Supervisor) */ +# define CTR_sig(context) REG_sig(ctr, context) +# define XER_sig(context) REG_sig(xer, context) /* Link register */ +# define LR_sig(context) REG_sig(lr, context) /* User's integer exception register */ +# define CR_sig(context) REG_sig(cr, context) /* Condition register */ +/* Float Registers access */ +# define FLOAT_sig(reg_num, context) FLOATREG_sig(fpregs[reg_num], context) +# define FPSCR_sig(context) ((double)FLOATREG_sig(fpscr, context)) +/* Exception Registers access */ +# define DAR_sig(context) EXCEPREG_sig(dar, context) /* Fault registers for coredump */ +# define DSISR_sig(context) EXCEPREG_sig(dsisr, context) +# define TRAP_sig(context) EXCEPREG_sig(exception, context) /* number of powerpc exception taken */ +#endif /* __APPLE__ */ + +int cpu_signal_handler(int host_signum, struct siginfo *info, + void *puc) +{ + struct ucontext *uc = puc; + unsigned long pc; + int is_write; + + pc = IAR_sig(uc); + is_write = 0; +#if 0 + /* ppc 4xx case */ + if (DSISR_sig(uc) & 0x00800000) + is_write = 1; +#else + if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000)) + is_write = 1; +#endif + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, &uc->uc_sigmask, puc); +} + +#elif defined(__alpha__) + +int cpu_signal_handler(int host_signum, struct siginfo *info, + void *puc) +{ + struct ucontext *uc = puc; + uint32_t *pc = uc->uc_mcontext.sc_pc; + uint32_t insn = *pc; + int is_write = 0; + + /* XXX: need kernel patch to get write flag faster */ + switch (insn >> 26) { + case 0x0d: // stw + case 0x0e: // stb + case 0x0f: // stq_u + case 0x24: // stf + case 0x25: // stg + case 0x26: // sts + case 0x27: // stt + case 0x2c: // stl + case 0x2d: // stq + case 0x2e: // stl_c + case 0x2f: // stq_c + is_write = 1; + } + + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, &uc->uc_sigmask, puc); +} +#elif defined(__sparc__) + +int cpu_signal_handler(int host_signum, struct siginfo *info, + void *puc) +{ + uint32_t *regs = (uint32_t *)(info + 1); + void *sigmask = (regs + 20); + unsigned long pc; + int is_write; + uint32_t insn; + + /* XXX: is there a standard glibc define ? */ + pc = regs[1]; + /* XXX: need kernel patch to get write flag faster */ + is_write = 0; + insn = *(uint32_t *)pc; + if ((insn >> 30) == 3) { + switch((insn >> 19) & 0x3f) { + case 0x05: // stb + case 0x06: // sth + case 0x04: // st + case 0x07: // std + case 0x24: // stf + case 0x27: // stdf + case 0x25: // stfsr + is_write = 1; + break; + } + } + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, sigmask, NULL); +} + +#elif defined(__arm__) + +int cpu_signal_handler(int host_signum, struct siginfo *info, + void *puc) +{ + struct ucontext *uc = puc; + unsigned long pc; + int is_write; + + pc = uc->uc_mcontext.gregs[R15]; + /* XXX: compute is_write */ + is_write = 0; + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, + &uc->uc_sigmask); +} + +#elif defined(__mc68000) + +int cpu_signal_handler(int host_signum, struct siginfo *info, + void *puc) +{ + struct ucontext *uc = puc; + unsigned long pc; + int is_write; + + pc = uc->uc_mcontext.gregs[16]; + /* XXX: compute is_write */ + is_write = 0; + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, + &uc->uc_sigmask, puc); +} + +#elif defined(__ia64) + +#ifndef __ISR_VALID + /* This ought to be in ... */ +# define __ISR_VALID 1 +#endif + +int cpu_signal_handler(int host_signum, struct siginfo *info, void *puc) +{ + struct ucontext *uc = puc; + unsigned long ip; + int is_write = 0; + + ip = uc->uc_mcontext.sc_ip; + switch (host_signum) { + case SIGILL: + case SIGFPE: + case SIGSEGV: + case SIGBUS: + case SIGTRAP: + if (info->si_code && (info->si_segvflags & __ISR_VALID)) + /* ISR.W (write-access) is bit 33: */ + is_write = (info->si_isr >> 33) & 1; + break; + + default: + break; + } + return handle_cpu_signal(ip, (unsigned long)info->si_addr, + is_write, + &uc->uc_sigmask, puc); +} + +#elif defined(__s390__) + +int cpu_signal_handler(int host_signum, struct siginfo *info, + void *puc) +{ + struct ucontext *uc = puc; + unsigned long pc; + int is_write; + + pc = uc->uc_mcontext.psw.addr; + /* XXX: compute is_write */ + is_write = 0; + return handle_cpu_signal(pc, (unsigned long)info->si_addr, + is_write, + &uc->uc_sigmask, puc); +} + +#else + +#error host CPU specific signal handler needed + +#endif + +#endif /* !defined(CONFIG_SOFTMMU) */ diff --git a/tools/ioemu/dis-asm.h b/tools/ioemu/dis-asm.h new file mode 100644 index 0000000000..73b4380b78 --- /dev/null +++ b/tools/ioemu/dis-asm.h @@ -0,0 +1,455 @@ +/* Interface between the opcode library and its callers. + Written by Cygnus Support, 1993. + + The opcode library (libopcodes.a) provides instruction decoders for + a large variety of instruction sets, callable with an identical + interface, for making instruction-processing programs more independent + of the instruction set being processed. */ + +#ifndef DIS_ASM_H +#define DIS_ASM_H + +#include +#include +#include +#include + +#define PARAMS(x) x +typedef void *PTR; +typedef uint64_t bfd_vma; +typedef int64_t bfd_signed_vma; +typedef uint8_t bfd_byte; +#define sprintf_vma(s,x) sprintf (s, "%0" PRIx64, x) + +#define BFD64 + +enum bfd_flavour { + bfd_target_unknown_flavour, + bfd_target_aout_flavour, + bfd_target_coff_flavour, + bfd_target_ecoff_flavour, + bfd_target_elf_flavour, + bfd_target_ieee_flavour, + bfd_target_nlm_flavour, + bfd_target_oasys_flavour, + bfd_target_tekhex_flavour, + bfd_target_srec_flavour, + bfd_target_ihex_flavour, + bfd_target_som_flavour, + bfd_target_os9k_flavour, + bfd_target_versados_flavour, + bfd_target_msdos_flavour, + bfd_target_evax_flavour +}; + +enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN }; + +enum bfd_architecture +{ + bfd_arch_unknown, /* File arch not known */ + bfd_arch_obscure, /* Arch known, not one of these */ + bfd_arch_m68k, /* Motorola 68xxx */ +#define bfd_mach_m68000 1 +#define bfd_mach_m68008 2 +#define bfd_mach_m68010 3 +#define bfd_mach_m68020 4 +#define bfd_mach_m68030 5 +#define bfd_mach_m68040 6 +#define bfd_mach_m68060 7 +#define bfd_mach_cpu32 8 +#define bfd_mach_mcf5200 9 +#define bfd_mach_mcf5206e 10 +#define bfd_mach_mcf5307 11 +#define bfd_mach_mcf5407 12 +#define bfd_mach_mcf528x 13 +#define bfd_mach_mcfv4e 14 +#define bfd_mach_mcf521x 15 +#define bfd_mach_mcf5249 16 +#define bfd_mach_mcf547x 17 +#define bfd_mach_mcf548x 18 + bfd_arch_vax, /* DEC Vax */ + bfd_arch_i960, /* Intel 960 */ + /* The order of the following is important. + lower number indicates a machine type that + only accepts a subset of the instructions + available to machines with higher numbers. + The exception is the "ca", which is + incompatible with all other machines except + "core". */ + +#define bfd_mach_i960_core 1 +#define bfd_mach_i960_ka_sa 2 +#define bfd_mach_i960_kb_sb 3 +#define bfd_mach_i960_mc 4 +#define bfd_mach_i960_xa 5 +#define bfd_mach_i960_ca 6 +#define bfd_mach_i960_jx 7 +#define bfd_mach_i960_hx 8 + + bfd_arch_a29k, /* AMD 29000 */ + bfd_arch_sparc, /* SPARC */ +#define bfd_mach_sparc 1 +/* The difference between v8plus and v9 is that v9 is a true 64 bit env. */ +#define bfd_mach_sparc_sparclet 2 +#define bfd_mach_sparc_sparclite 3 +#define bfd_mach_sparc_v8plus 4 +#define bfd_mach_sparc_v8plusa 5 /* with ultrasparc add'ns. */ +#define bfd_mach_sparc_sparclite_le 6 +#define bfd_mach_sparc_v9 7 +#define bfd_mach_sparc_v9a 8 /* with ultrasparc add'ns. */ +#define bfd_mach_sparc_v8plusb 9 /* with cheetah add'ns. */ +#define bfd_mach_sparc_v9b 10 /* with cheetah add'ns. */ +/* Nonzero if MACH has the v9 instruction set. */ +#define bfd_mach_sparc_v9_p(mach) \ + ((mach) >= bfd_mach_sparc_v8plus && (mach) <= bfd_mach_sparc_v9b \ + && (mach) != bfd_mach_sparc_sparclite_le) + bfd_arch_mips, /* MIPS Rxxxx */ +#define bfd_mach_mips3000 3000 +#define bfd_mach_mips3900 3900 +#define bfd_mach_mips4000 4000 +#define bfd_mach_mips4010 4010 +#define bfd_mach_mips4100 4100 +#define bfd_mach_mips4300 4300 +#define bfd_mach_mips4400 4400 +#define bfd_mach_mips4600 4600 +#define bfd_mach_mips4650 4650 +#define bfd_mach_mips5000 5000 +#define bfd_mach_mips6000 6000 +#define bfd_mach_mips8000 8000 +#define bfd_mach_mips10000 10000 +#define bfd_mach_mips16 16 + bfd_arch_i386, /* Intel 386 */ +#define bfd_mach_i386_i386 0 +#define bfd_mach_i386_i8086 1 +#define bfd_mach_i386_i386_intel_syntax 2 +#define bfd_mach_x86_64 3 +#define bfd_mach_x86_64_intel_syntax 4 + bfd_arch_we32k, /* AT&T WE32xxx */ + bfd_arch_tahoe, /* CCI/Harris Tahoe */ + bfd_arch_i860, /* Intel 860 */ + bfd_arch_romp, /* IBM ROMP PC/RT */ + bfd_arch_alliant, /* Alliant */ + bfd_arch_convex, /* Convex */ + bfd_arch_m88k, /* Motorola 88xxx */ + bfd_arch_pyramid, /* Pyramid Technology */ + bfd_arch_h8300, /* Hitachi H8/300 */ +#define bfd_mach_h8300 1 +#define bfd_mach_h8300h 2 +#define bfd_mach_h8300s 3 + bfd_arch_powerpc, /* PowerPC */ +#define bfd_mach_ppc 0 +#define bfd_mach_ppc64 1 +#define bfd_mach_ppc_403 403 +#define bfd_mach_ppc_403gc 4030 +#define bfd_mach_ppc_505 505 +#define bfd_mach_ppc_601 601 +#define bfd_mach_ppc_602 602 +#define bfd_mach_ppc_603 603 +#define bfd_mach_ppc_ec603e 6031 +#define bfd_mach_ppc_604 604 +#define bfd_mach_ppc_620 620 +#define bfd_mach_ppc_630 630 +#define bfd_mach_ppc_750 750 +#define bfd_mach_ppc_860 860 +#define bfd_mach_ppc_a35 35 +#define bfd_mach_ppc_rs64ii 642 +#define bfd_mach_ppc_rs64iii 643 +#define bfd_mach_ppc_7400 7400 + bfd_arch_rs6000, /* IBM RS/6000 */ + bfd_arch_hppa, /* HP PA RISC */ + bfd_arch_d10v, /* Mitsubishi D10V */ + bfd_arch_z8k, /* Zilog Z8000 */ +#define bfd_mach_z8001 1 +#define bfd_mach_z8002 2 + bfd_arch_h8500, /* Hitachi H8/500 */ + bfd_arch_sh, /* Hitachi SH */ +#define bfd_mach_sh 1 +#define bfd_mach_sh2 0x20 +#define bfd_mach_sh_dsp 0x2d +#define bfd_mach_sh2a 0x2a +#define bfd_mach_sh2a_nofpu 0x2b +#define bfd_mach_sh2e 0x2e +#define bfd_mach_sh3 0x30 +#define bfd_mach_sh3_nommu 0x31 +#define bfd_mach_sh3_dsp 0x3d +#define bfd_mach_sh3e 0x3e +#define bfd_mach_sh4 0x40 +#define bfd_mach_sh4_nofpu 0x41 +#define bfd_mach_sh4_nommu_nofpu 0x42 +#define bfd_mach_sh4a 0x4a +#define bfd_mach_sh4a_nofpu 0x4b +#define bfd_mach_sh4al_dsp 0x4d +#define bfd_mach_sh5 0x50 + bfd_arch_alpha, /* Dec Alpha */ + bfd_arch_arm, /* Advanced Risc Machines ARM */ +#define bfd_mach_arm_2 1 +#define bfd_mach_arm_2a 2 +#define bfd_mach_arm_3 3 +#define bfd_mach_arm_3M 4 +#define bfd_mach_arm_4 5 +#define bfd_mach_arm_4T 6 + bfd_arch_ns32k, /* National Semiconductors ns32000 */ + bfd_arch_w65, /* WDC 65816 */ + bfd_arch_tic30, /* Texas Instruments TMS320C30 */ + bfd_arch_v850, /* NEC V850 */ +#define bfd_mach_v850 0 + bfd_arch_arc, /* Argonaut RISC Core */ +#define bfd_mach_arc_base 0 + bfd_arch_m32r, /* Mitsubishi M32R/D */ +#define bfd_mach_m32r 0 /* backwards compatibility */ + bfd_arch_mn10200, /* Matsushita MN10200 */ + bfd_arch_mn10300, /* Matsushita MN10300 */ + bfd_arch_last + }; + +typedef struct symbol_cache_entry +{ + const char *name; + union + { + PTR p; + bfd_vma i; + } udata; +} asymbol; + +typedef int (*fprintf_ftype) PARAMS((FILE*, const char*, ...)); + +enum dis_insn_type { + dis_noninsn, /* Not a valid instruction */ + dis_nonbranch, /* Not a branch instruction */ + dis_branch, /* Unconditional branch */ + dis_condbranch, /* Conditional branch */ + dis_jsr, /* Jump to subroutine */ + dis_condjsr, /* Conditional jump to subroutine */ + dis_dref, /* Data reference instruction */ + dis_dref2 /* Two data references in instruction */ +}; + +/* This struct is passed into the instruction decoding routine, + and is passed back out into each callback. The various fields are used + for conveying information from your main routine into your callbacks, + for passing information into the instruction decoders (such as the + addresses of the callback functions), or for passing information + back from the instruction decoders to their callers. + + It must be initialized before it is first passed; this can be done + by hand, or using one of the initialization macros below. */ + +typedef struct disassemble_info { + fprintf_ftype fprintf_func; + FILE *stream; + PTR application_data; + + /* Target description. We could replace this with a pointer to the bfd, + but that would require one. There currently isn't any such requirement + so to avoid introducing one we record these explicitly. */ + /* The bfd_flavour. This can be bfd_target_unknown_flavour. */ + enum bfd_flavour flavour; + /* The bfd_arch value. */ + enum bfd_architecture arch; + /* The bfd_mach value. */ + unsigned long mach; + /* Endianness (for bi-endian cpus). Mono-endian cpus can ignore this. */ + enum bfd_endian endian; + + /* An array of pointers to symbols either at the location being disassembled + or at the start of the function being disassembled. The array is sorted + so that the first symbol is intended to be the one used. The others are + present for any misc. purposes. This is not set reliably, but if it is + not NULL, it is correct. */ + asymbol **symbols; + /* Number of symbols in array. */ + int num_symbols; + + /* For use by the disassembler. + The top 16 bits are reserved for public use (and are documented here). + The bottom 16 bits are for the internal use of the disassembler. */ + unsigned long flags; +#define INSN_HAS_RELOC 0x80000000 + PTR private_data; + + /* Function used to get bytes to disassemble. MEMADDR is the + address of the stuff to be disassembled, MYADDR is the address to + put the bytes in, and LENGTH is the number of bytes to read. + INFO is a pointer to this struct. + Returns an errno value or 0 for success. */ + int (*read_memory_func) + PARAMS ((bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info)); + + /* Function which should be called if we get an error that we can't + recover from. STATUS is the errno value from read_memory_func and + MEMADDR is the address that we were trying to read. INFO is a + pointer to this struct. */ + void (*memory_error_func) + PARAMS ((int status, bfd_vma memaddr, struct disassemble_info *info)); + + /* Function called to print ADDR. */ + void (*print_address_func) + PARAMS ((bfd_vma addr, struct disassemble_info *info)); + + /* Function called to determine if there is a symbol at the given ADDR. + If there is, the function returns 1, otherwise it returns 0. + This is used by ports which support an overlay manager where + the overlay number is held in the top part of an address. In + some circumstances we want to include the overlay number in the + address, (normally because there is a symbol associated with + that address), but sometimes we want to mask out the overlay bits. */ + int (* symbol_at_address_func) + PARAMS ((bfd_vma addr, struct disassemble_info * info)); + + /* These are for buffer_read_memory. */ + bfd_byte *buffer; + bfd_vma buffer_vma; + int buffer_length; + + /* This variable may be set by the instruction decoder. It suggests + the number of bytes objdump should display on a single line. If + the instruction decoder sets this, it should always set it to + the same value in order to get reasonable looking output. */ + int bytes_per_line; + + /* the next two variables control the way objdump displays the raw data */ + /* For example, if bytes_per_line is 8 and bytes_per_chunk is 4, the */ + /* output will look like this: + 00: 00000000 00000000 + with the chunks displayed according to "display_endian". */ + int bytes_per_chunk; + enum bfd_endian display_endian; + + /* Results from instruction decoders. Not all decoders yet support + this information. This info is set each time an instruction is + decoded, and is only valid for the last such instruction. + + To determine whether this decoder supports this information, set + insn_info_valid to 0, decode an instruction, then check it. */ + + char insn_info_valid; /* Branch info has been set. */ + char branch_delay_insns; /* How many sequential insn's will run before + a branch takes effect. (0 = normal) */ + char data_size; /* Size of data reference in insn, in bytes */ + enum dis_insn_type insn_type; /* Type of instruction */ + bfd_vma target; /* Target address of branch or dref, if known; + zero if unknown. */ + bfd_vma target2; /* Second target address for dref2 */ + + /* Command line options specific to the target disassembler. */ + char * disassembler_options; + +} disassemble_info; + + +/* Standard disassemblers. Disassemble one instruction at the given + target address. Return number of bytes processed. */ +typedef int (*disassembler_ftype) + PARAMS((bfd_vma, disassemble_info *)); + +extern int print_insn_big_mips PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_little_mips PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_i386 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_m68k PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_z8001 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_z8002 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_h8300 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_h8300h PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_h8300s PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_h8500 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_alpha PARAMS ((bfd_vma, disassemble_info*)); +extern disassembler_ftype arc_get_disassembler PARAMS ((int, int)); +extern int print_insn_arm PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_sparc PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_big_a29k PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_little_a29k PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_i960 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_sh PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_shl PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_hppa PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_m32r PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_m88k PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_mn10200 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_mn10300 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_ns32k PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_big_powerpc PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_little_powerpc PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_rs6000 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_w65 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_d10v PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_v850 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_tic30 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_ppc PARAMS ((bfd_vma, disassemble_info*)); + +#if 0 +/* Fetch the disassembler for a given BFD, if that support is available. */ +extern disassembler_ftype disassembler PARAMS ((bfd *)); +#endif + + +/* This block of definitions is for particular callers who read instructions + into a buffer before calling the instruction decoder. */ + +/* Here is a function which callers may wish to use for read_memory_func. + It gets bytes from a buffer. */ +extern int buffer_read_memory + PARAMS ((bfd_vma, bfd_byte *, int, struct disassemble_info *)); + +/* This function goes with buffer_read_memory. + It prints a message using info->fprintf_func and info->stream. */ +extern void perror_memory PARAMS ((int, bfd_vma, struct disassemble_info *)); + + +/* Just print the address in hex. This is included for completeness even + though both GDB and objdump provide their own (to print symbolic + addresses). */ +extern void generic_print_address + PARAMS ((bfd_vma, struct disassemble_info *)); + +/* Always true. */ +extern int generic_symbol_at_address + PARAMS ((bfd_vma, struct disassemble_info *)); + +/* Macro to initialize a disassemble_info struct. This should be called + by all applications creating such a struct. */ +#define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \ + (INFO).flavour = bfd_target_unknown_flavour, \ + (INFO).arch = bfd_arch_unknown, \ + (INFO).mach = 0, \ + (INFO).endian = BFD_ENDIAN_UNKNOWN, \ + INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) + +/* Call this macro to initialize only the internal variables for the + disassembler. Architecture dependent things such as byte order, or machine + variant are not touched by this macro. This makes things much easier for + GDB which must initialize these things seperatly. */ + +#define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \ + (INFO).fprintf_func = (FPRINTF_FUNC), \ + (INFO).stream = (STREAM), \ + (INFO).symbols = NULL, \ + (INFO).num_symbols = 0, \ + (INFO).buffer = NULL, \ + (INFO).buffer_vma = 0, \ + (INFO).buffer_length = 0, \ + (INFO).read_memory_func = buffer_read_memory, \ + (INFO).memory_error_func = perror_memory, \ + (INFO).print_address_func = generic_print_address, \ + (INFO).symbol_at_address_func = generic_symbol_at_address, \ + (INFO).flags = 0, \ + (INFO).bytes_per_line = 0, \ + (INFO).bytes_per_chunk = 0, \ + (INFO).display_endian = BFD_ENDIAN_UNKNOWN, \ + (INFO).disassembler_options = NULL, \ + (INFO).insn_info_valid = 0 + +#define _(x) x +#define ATTRIBUTE_UNUSED __attribute__((unused)) + +/* from libbfd */ + +bfd_vma bfd_getl32 (const bfd_byte *addr); +bfd_vma bfd_getb32 (const bfd_byte *addr); +bfd_vma bfd_getl16 (const bfd_byte *addr); +bfd_vma bfd_getb16 (const bfd_byte *addr); +typedef enum bfd_boolean {false, true} boolean; +typedef boolean bfd_boolean; + +#endif /* ! defined (DIS_ASM_H) */ diff --git a/tools/ioemu/disas.c b/tools/ioemu/disas.c new file mode 100644 index 0000000000..c38da08fd1 --- /dev/null +++ b/tools/ioemu/disas.c @@ -0,0 +1,413 @@ +/* General "disassemble this chunk" code. Used for debugging. */ +#include "config.h" +#include "dis-asm.h" +#include "elf.h" +#include + +#include "cpu.h" +#include "exec-all.h" +#include "disas.h" + +/* Filled in by elfload.c. Simplistic, but will do for now. */ +struct syminfo *syminfos = NULL; + +/* Get LENGTH bytes from info's buffer, at target address memaddr. + Transfer them to myaddr. */ +int +buffer_read_memory (memaddr, myaddr, length, info) + bfd_vma memaddr; + bfd_byte *myaddr; + int length; + struct disassemble_info *info; +{ + if (memaddr < info->buffer_vma + || memaddr + length > info->buffer_vma + info->buffer_length) + /* Out of bounds. Use EIO because GDB uses it. */ + return EIO; + memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length); + return 0; +} + +/* Get LENGTH bytes from info's buffer, at target address memaddr. + Transfer them to myaddr. */ +static int +target_read_memory (bfd_vma memaddr, + bfd_byte *myaddr, + int length, + struct disassemble_info *info) +{ + int i; + for(i = 0; i < length; i++) { + myaddr[i] = ldub_code(memaddr + i); + } + return 0; +} + +/* Print an error message. We can assume that this is in response to + an error return from buffer_read_memory. */ +void +perror_memory (status, memaddr, info) + int status; + bfd_vma memaddr; + struct disassemble_info *info; +{ + if (status != EIO) + /* Can't happen. */ + (*info->fprintf_func) (info->stream, "Unknown error %d\n", status); + else + /* Actually, address between memaddr and memaddr + len was + out of bounds. */ + (*info->fprintf_func) (info->stream, + "Address 0x%llx is out of bounds.\n", memaddr); +} + +/* This could be in a separate file, to save miniscule amounts of space + in statically linked executables. */ + +/* Just print the address is hex. This is included for completeness even + though both GDB and objdump provide their own (to print symbolic + addresses). */ + +void +generic_print_address (addr, info) + bfd_vma addr; + struct disassemble_info *info; +{ + (*info->fprintf_func) (info->stream, "0x%llx", addr); +} + +/* Just return the given address. */ + +int +generic_symbol_at_address (addr, info) + bfd_vma addr; + struct disassemble_info * info; +{ + return 1; +} + +bfd_vma bfd_getl32 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0]; + v |= (unsigned long) addr[1] << 8; + v |= (unsigned long) addr[2] << 16; + v |= (unsigned long) addr[3] << 24; + return (bfd_vma) v; +} + +bfd_vma bfd_getb32 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0] << 24; + v |= (unsigned long) addr[1] << 16; + v |= (unsigned long) addr[2] << 8; + v |= (unsigned long) addr[3]; + return (bfd_vma) v; +} + +bfd_vma bfd_getl16 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0]; + v |= (unsigned long) addr[1] << 8; + return (bfd_vma) v; +} + +bfd_vma bfd_getb16 (const bfd_byte *addr) +{ + unsigned long v; + + v = (unsigned long) addr[0] << 24; + v |= (unsigned long) addr[1] << 16; + return (bfd_vma) v; +} + +#ifdef TARGET_ARM +static int +print_insn_thumb1(bfd_vma pc, disassemble_info *info) +{ + return print_insn_arm(pc | 1, info); +} +#endif + +/* Disassemble this for me please... (debugging). 'flags' has teh following + values: + i386 - nonzero means 16 bit code + arm - nonzero means thumb code + ppc - nonzero means little endian + other targets - unused + */ +void target_disas(FILE *out, target_ulong code, target_ulong size, int flags) +{ + target_ulong pc; + int count; + struct disassemble_info disasm_info; + int (*print_insn)(bfd_vma pc, disassemble_info *info); + + INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf); + + disasm_info.read_memory_func = target_read_memory; + disasm_info.buffer_vma = code; + disasm_info.buffer_length = size; + +#ifdef TARGET_WORDS_BIGENDIAN + disasm_info.endian = BFD_ENDIAN_BIG; +#else + disasm_info.endian = BFD_ENDIAN_LITTLE; +#endif +#if defined(TARGET_I386) + if (flags == 2) + disasm_info.mach = bfd_mach_x86_64; + else if (flags == 1) + disasm_info.mach = bfd_mach_i386_i8086; + else + disasm_info.mach = bfd_mach_i386_i386; + print_insn = print_insn_i386; +#elif defined(TARGET_ARM) + if (flags) + print_insn = print_insn_thumb1; + else + print_insn = print_insn_arm; +#elif defined(TARGET_SPARC) + print_insn = print_insn_sparc; +#ifdef TARGET_SPARC64 + disasm_info.mach = bfd_mach_sparc_v9b; +#endif +#elif defined(TARGET_PPC) + if (flags) + disasm_info.endian = BFD_ENDIAN_LITTLE; +#ifdef TARGET_PPC64 + disasm_info.mach = bfd_mach_ppc64; +#else + disasm_info.mach = bfd_mach_ppc; +#endif + print_insn = print_insn_ppc; +#elif defined(TARGET_MIPS) +#ifdef TARGET_WORDS_BIGENDIAN + print_insn = print_insn_big_mips; +#else + print_insn = print_insn_little_mips; +#endif +#elif defined(TARGET_M68K) + print_insn = print_insn_m68k; +#elif defined(TARGET_SH4) + disasm_info.mach = bfd_mach_sh4; + print_insn = print_insn_sh; +#else + fprintf(out, "0x" TARGET_FMT_lx + ": Asm output not supported on this arch\n", code); + return; +#endif + + for (pc = code; pc < code + size; pc += count) { + fprintf(out, "0x" TARGET_FMT_lx ": ", pc); + count = print_insn(pc, &disasm_info); +#if 0 + { + int i; + uint8_t b; + fprintf(out, " {"); + for(i = 0; i < count; i++) { + target_read_memory(pc + i, &b, 1, &disasm_info); + fprintf(out, " %02x", b); + } + fprintf(out, " }"); + } +#endif + fprintf(out, "\n"); + if (count < 0) + break; + } +} + +/* Disassemble this for me please... (debugging). */ +void disas(FILE *out, void *code, unsigned long size) +{ + unsigned long pc; + int count; + struct disassemble_info disasm_info; + int (*print_insn)(bfd_vma pc, disassemble_info *info); + + INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf); + + disasm_info.buffer = code; + disasm_info.buffer_vma = (unsigned long)code; + disasm_info.buffer_length = size; + +#ifdef WORDS_BIGENDIAN + disasm_info.endian = BFD_ENDIAN_BIG; +#else + disasm_info.endian = BFD_ENDIAN_LITTLE; +#endif +#if defined(__i386__) + disasm_info.mach = bfd_mach_i386_i386; + print_insn = print_insn_i386; +#elif defined(__x86_64__) + disasm_info.mach = bfd_mach_x86_64; + print_insn = print_insn_i386; +#elif defined(__powerpc__) + print_insn = print_insn_ppc; +#elif defined(__alpha__) + print_insn = print_insn_alpha; +#elif defined(__sparc__) + print_insn = print_insn_sparc; +#elif defined(__arm__) + print_insn = print_insn_arm; +#elif defined(__MIPSEB__) + print_insn = print_insn_big_mips; +#elif defined(__MIPSEL__) + print_insn = print_insn_little_mips; +#elif defined(__m68k__) + print_insn = print_insn_m68k; +#else + fprintf(out, "0x%lx: Asm output not supported on this arch\n", + (long) code); + return; +#endif + for (pc = (unsigned long)code; pc < (unsigned long)code + size; pc += count) { + fprintf(out, "0x%08lx: ", pc); +#ifdef __arm__ + /* since data are included in the code, it is better to + display code data too */ + if (is_host) { + fprintf(out, "%08x ", (int)bfd_getl32((const bfd_byte *)pc)); + } +#endif + count = print_insn(pc, &disasm_info); + fprintf(out, "\n"); + if (count < 0) + break; + } +} + +/* Look up symbol for debugging purpose. Returns "" if unknown. */ +const char *lookup_symbol(target_ulong orig_addr) +{ + unsigned int i; + /* Hack, because we know this is x86. */ + Elf32_Sym *sym; + struct syminfo *s; + target_ulong addr; + + for (s = syminfos; s; s = s->next) { + sym = s->disas_symtab; + for (i = 0; i < s->disas_num_syms; i++) { + if (sym[i].st_shndx == SHN_UNDEF + || sym[i].st_shndx >= SHN_LORESERVE) + continue; + + if (ELF_ST_TYPE(sym[i].st_info) != STT_FUNC) + continue; + + addr = sym[i].st_value; +#ifdef TARGET_ARM + /* The bottom address bit marks a Thumb symbol. */ + addr &= ~(target_ulong)1; +#endif + if (orig_addr >= addr + && orig_addr < addr + sym[i].st_size) + return s->disas_strtab + sym[i].st_name; + } + } + return ""; +} + +#if !defined(CONFIG_USER_ONLY) + +void term_vprintf(const char *fmt, va_list ap); +void term_printf(const char *fmt, ...); + +static int monitor_disas_is_physical; +static CPUState *monitor_disas_env; + +static int +monitor_read_memory (memaddr, myaddr, length, info) + bfd_vma memaddr; + bfd_byte *myaddr; + int length; + struct disassemble_info *info; +{ + if (monitor_disas_is_physical) { + cpu_physical_memory_rw(memaddr, myaddr, length, 0); + } else { + cpu_memory_rw_debug(monitor_disas_env, memaddr,myaddr, length, 0); + } + return 0; +} + +static int monitor_fprintf(FILE *stream, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + term_vprintf(fmt, ap); + va_end(ap); + return 0; +} + +void monitor_disas(CPUState *env, + target_ulong pc, int nb_insn, int is_physical, int flags) +{ + int count, i; + struct disassemble_info disasm_info; + int (*print_insn)(bfd_vma pc, disassemble_info *info); + + INIT_DISASSEMBLE_INFO(disasm_info, NULL, monitor_fprintf); + + monitor_disas_env = env; + monitor_disas_is_physical = is_physical; + disasm_info.read_memory_func = monitor_read_memory; + + disasm_info.buffer_vma = pc; + +#ifdef TARGET_WORDS_BIGENDIAN + disasm_info.endian = BFD_ENDIAN_BIG; +#else + disasm_info.endian = BFD_ENDIAN_LITTLE; +#endif +#if defined(TARGET_I386) + if (flags == 2) + disasm_info.mach = bfd_mach_x86_64; + else if (flags == 1) + disasm_info.mach = bfd_mach_i386_i8086; + else + disasm_info.mach = bfd_mach_i386_i386; + print_insn = print_insn_i386; +#elif defined(TARGET_ARM) + print_insn = print_insn_arm; +#elif defined(TARGET_SPARC) + print_insn = print_insn_sparc; +#elif defined(TARGET_PPC) +#ifdef TARGET_PPC64 + disasm_info.mach = bfd_mach_ppc64; +#else + disasm_info.mach = bfd_mach_ppc; +#endif + print_insn = print_insn_ppc; +#elif defined(TARGET_MIPS) +#ifdef TARGET_WORDS_BIGENDIAN + print_insn = print_insn_big_mips; +#else + print_insn = print_insn_little_mips; +#endif +#elif defined(TARGET_M68K) + print_insn = print_insn_m68k; +#else + term_printf("0x" TARGET_FMT_lx + ": Asm output not supported on this arch\n", pc); + return; +#endif + + for(i = 0; i < nb_insn; i++) { + term_printf("0x" TARGET_FMT_lx ": ", pc); + count = print_insn(pc, &disasm_info); + term_printf("\n"); + if (count < 0) + break; + pc += count; + } +} +#endif diff --git a/tools/ioemu/disas.h b/tools/ioemu/disas.h new file mode 100644 index 0000000000..e85b4f6b64 --- /dev/null +++ b/tools/ioemu/disas.h @@ -0,0 +1,23 @@ +#ifndef _QEMU_DISAS_H +#define _QEMU_DISAS_H + +#ifndef CONFIG_DM +/* Disassemble this for me please... (debugging). */ +void disas(FILE *out, void *code, unsigned long size); +void target_disas(FILE *out, target_ulong code, target_ulong size, int flags); +void monitor_disas(CPUState *env, + target_ulong pc, int nb_insn, int is_physical, int flags); + +/* Look up symbol for debugging purpose. Returns "" if unknown. */ +const char *lookup_symbol(target_ulong orig_addr); + +/* Filled in by elfload.c. Simplistic, but will do for now. */ +extern struct syminfo { + unsigned int disas_num_syms; + void *disas_symtab; + const char *disas_strtab; + struct syminfo *next; +} *syminfos; +#endif /* !CONFIG_DM */ + +#endif /* _QEMU_DISAS_H */ diff --git a/tools/ioemu/dyngen-exec.h b/tools/ioemu/dyngen-exec.h new file mode 100644 index 0000000000..6952c3a2c8 --- /dev/null +++ b/tools/ioemu/dyngen-exec.h @@ -0,0 +1,257 @@ +/* + * dyngen defines for micro operation code + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#if !defined(__DYNGEN_EXEC_H__) +#define __DYNGEN_EXEC_H__ + +/* prevent Solaris from trying to typedef FILE in gcc's + include/floatingpoint.h which will conflict with the + definition down below */ +#ifdef __sun__ +#define _FILEDEFED +#endif + +/* NOTE: standard headers should be used with special care at this + point because host CPU registers are used as global variables. Some + host headers do not allow that. */ +#include + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +/* XXX may be done for all 64 bits targets ? */ +#if defined (__x86_64__) || defined(__ia64) +typedef unsigned long uint64_t; +#else +typedef unsigned long long uint64_t; +#endif + +/* if Solaris/__sun__, don't typedef int8_t, as it will be typedef'd + prior to this and will cause an error in compliation, conflicting + with /usr/include/sys/int_types.h, line 75 */ +#ifndef __sun__ +typedef signed char int8_t; +#endif +typedef signed short int16_t; +typedef signed int int32_t; +#if defined (__x86_64__) || defined(__ia64) +typedef signed long int64_t; +#else +typedef signed long long int64_t; +#endif + +#define INT8_MIN (-128) +#define INT16_MIN (-32767-1) +#define INT32_MIN (-2147483647-1) +#define INT64_MIN (-(int64_t)(9223372036854775807)-1) +#define INT8_MAX (127) +#define INT16_MAX (32767) +#define INT32_MAX (2147483647) +#define INT64_MAX ((int64_t)(9223372036854775807)) +#define UINT8_MAX (255) +#define UINT16_MAX (65535) +#define UINT32_MAX (4294967295U) +#define UINT64_MAX ((uint64_t)(18446744073709551615)) + +typedef struct FILE FILE; +extern int fprintf(FILE *, const char *, ...); +extern int printf(const char *, ...); +#undef NULL +#define NULL 0 + +#ifdef __i386__ +#define AREG0 "ebp" +#define AREG1 "ebx" +#define AREG2 "esi" +#define AREG3 "edi" +#endif +#ifdef __x86_64__ +#define AREG0 "rbp" +#define AREG1 "rbx" +#define AREG2 "r12" +#define AREG3 "r13" +//#define AREG4 "r14" +//#define AREG5 "r15" +#endif +#ifdef __powerpc__ +#define AREG0 "r27" +#define AREG1 "r24" +#define AREG2 "r25" +#define AREG3 "r26" +/* XXX: suppress this hack */ +#if defined(CONFIG_USER_ONLY) +#define AREG4 "r16" +#define AREG5 "r17" +#define AREG6 "r18" +#define AREG7 "r19" +#define AREG8 "r20" +#define AREG9 "r21" +#define AREG10 "r22" +#define AREG11 "r23" +#endif +#define USE_INT_TO_FLOAT_HELPERS +#define BUGGY_GCC_DIV64 +#endif +#ifdef __arm__ +#define AREG0 "r7" +#define AREG1 "r4" +#define AREG2 "r5" +#define AREG3 "r6" +#endif +#ifdef __mips__ +#define AREG0 "s3" +#define AREG1 "s0" +#define AREG2 "s1" +#define AREG3 "s2" +#endif +#ifdef __sparc__ +#define AREG0 "g6" +#define AREG1 "g1" +#define AREG2 "g2" +#define AREG3 "g3" +#define AREG4 "l0" +#define AREG5 "l1" +#define AREG6 "l2" +#define AREG7 "l3" +#define AREG8 "l4" +#define AREG9 "l5" +#define AREG10 "l6" +#define AREG11 "l7" +#define USE_FP_CONVERT +#endif +#ifdef __s390__ +#define AREG0 "r10" +#define AREG1 "r7" +#define AREG2 "r8" +#define AREG3 "r9" +#endif +#ifdef __alpha__ +/* Note $15 is the frame pointer, so anything in op-i386.c that would + require a frame pointer, like alloca, would probably loose. */ +#define AREG0 "$15" +#define AREG1 "$9" +#define AREG2 "$10" +#define AREG3 "$11" +#define AREG4 "$12" +#define AREG5 "$13" +#define AREG6 "$14" +#endif +#ifdef __mc68000 +#define AREG0 "%a5" +#define AREG1 "%a4" +#define AREG2 "%d7" +#define AREG3 "%d6" +#define AREG4 "%d5" +#endif +#ifdef __ia64__ +#define AREG0 "r7" +#define AREG1 "r4" +#define AREG2 "r5" +#define AREG3 "r6" +#endif + +/* force GCC to generate only one epilog at the end of the function */ +#define FORCE_RET() asm volatile (""); + +#ifndef OPPROTO +#define OPPROTO +#endif + +#define xglue(x, y) x ## y +#define glue(x, y) xglue(x, y) +#define stringify(s) tostring(s) +#define tostring(s) #s + +#ifdef __alpha__ +/* the symbols are considered non exported so a br immediate is generated */ +#define __hidden __attribute__((visibility("hidden"))) +#else +#define __hidden +#endif + +#if defined(__alpha__) +/* Suggested by Richard Henderson. This will result in code like + ldah $0,__op_param1($29) !gprelhigh + lda $0,__op_param1($0) !gprellow + We can then conveniently change $29 to $31 and adapt the offsets to + emit the appropriate constant. */ +extern int __op_param1 __hidden; +extern int __op_param2 __hidden; +extern int __op_param3 __hidden; +#define PARAM1 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param1)); _r; }) +#define PARAM2 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param2)); _r; }) +#define PARAM3 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param3)); _r; }) +#else +#if defined(__APPLE__) +static int __op_param1, __op_param2, __op_param3; +#else +extern int __op_param1, __op_param2, __op_param3; +#endif +#define PARAM1 ((long)(&__op_param1)) +#define PARAM2 ((long)(&__op_param2)) +#define PARAM3 ((long)(&__op_param3)) +#endif /* !defined(__alpha__) */ + +extern int __op_jmp0, __op_jmp1, __op_jmp2, __op_jmp3; + +#if defined(_WIN32) || defined(__APPLE__) +#define ASM_NAME(x) "_" #x +#else +#define ASM_NAME(x) #x +#endif + +#ifdef __i386__ +#define EXIT_TB() asm volatile ("ret") +#define GOTO_LABEL_PARAM(n) asm volatile ("jmp " ASM_NAME(__op_gen_label) #n) +#endif +#ifdef __x86_64__ +#define EXIT_TB() asm volatile ("ret") +#define GOTO_LABEL_PARAM(n) asm volatile ("jmp " ASM_NAME(__op_gen_label) #n) +#endif +#ifdef __powerpc__ +#define EXIT_TB() asm volatile ("blr") +#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n) +#endif +#ifdef __s390__ +#define EXIT_TB() asm volatile ("br %r14") +#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n) +#endif +#ifdef __alpha__ +#define EXIT_TB() asm volatile ("ret") +#endif +#ifdef __ia64__ +#define EXIT_TB() asm volatile ("br.ret.sptk.many b0;;") +#define GOTO_LABEL_PARAM(n) asm volatile ("br.sptk.many " \ + ASM_NAME(__op_gen_label) #n) +#endif +#ifdef __sparc__ +#define EXIT_TB() asm volatile ("jmpl %i0 + 8, %g0\n" \ + "nop") +#define GOTO_LABEL_PARAM(n) asm volatile ( \ + "set " ASM_NAME(__op_gen_label) #n ", %g1; jmp %g1; nop") +#endif +#ifdef __arm__ +#define EXIT_TB() asm volatile ("b exec_loop") +#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n) +#endif +#ifdef __mc68000 +#define EXIT_TB() asm volatile ("rts") +#endif + +#endif /* !defined(__DYNGEN_EXEC_H__) */ diff --git a/tools/ioemu/dyngen-op.h b/tools/ioemu/dyngen-op.h new file mode 100644 index 0000000000..f77a4756f7 --- /dev/null +++ b/tools/ioemu/dyngen-op.h @@ -0,0 +1,9 @@ +static inline int gen_new_label(void) +{ + return nb_gen_labels++; +} + +static inline void gen_set_label(int n) +{ + gen_labels[n] = gen_opc_ptr - gen_opc_buf; +} diff --git a/tools/ioemu/dyngen.c b/tools/ioemu/dyngen.c new file mode 100644 index 0000000000..c1f348a94f --- /dev/null +++ b/tools/ioemu/dyngen.c @@ -0,0 +1,2550 @@ +/* + * Generic Dynamic compiler generator + * + * Copyright (c) 2003 Fabrice Bellard + * + * The COFF object format support was extracted from Kazu's QEMU port + * to Win32. + * + * Mach-O Support by Matt Reda and Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "config-host.h" + +/* NOTE: we test CONFIG_WIN32 instead of _WIN32 to enabled cross + compilation */ +#if defined(CONFIG_WIN32) +#define CONFIG_FORMAT_COFF +#elif defined(CONFIG_DARWIN) +#define CONFIG_FORMAT_MACH +#else +#define CONFIG_FORMAT_ELF +#endif + +#ifdef CONFIG_FORMAT_ELF + +/* elf format definitions. We use these macros to test the CPU to + allow cross compilation (this tool must be ran on the build + platform) */ +#if defined(HOST_I386) + +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_386 +#define elf_check_arch(x) ( ((x) == EM_386) || ((x) == EM_486) ) +#undef ELF_USES_RELOCA + +#elif defined(HOST_X86_64) + +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_X86_64 +#define elf_check_arch(x) ((x) == EM_X86_64) +#define ELF_USES_RELOCA + +#elif defined(HOST_PPC) + +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_PPC +#define elf_check_arch(x) ((x) == EM_PPC) +#define ELF_USES_RELOCA + +#elif defined(HOST_S390) + +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_S390 +#define elf_check_arch(x) ((x) == EM_S390) +#define ELF_USES_RELOCA + +#elif defined(HOST_ALPHA) + +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_ALPHA +#define elf_check_arch(x) ((x) == EM_ALPHA) +#define ELF_USES_RELOCA + +#elif defined(HOST_IA64) + +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_IA_64 +#define elf_check_arch(x) ((x) == EM_IA_64) +#define ELF_USES_RELOCA + +#elif defined(HOST_SPARC) + +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_SPARC +#define elf_check_arch(x) ((x) == EM_SPARC || (x) == EM_SPARC32PLUS) +#define ELF_USES_RELOCA + +#elif defined(HOST_SPARC64) + +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_SPARCV9 +#define elf_check_arch(x) ((x) == EM_SPARCV9) +#define ELF_USES_RELOCA + +#elif defined(HOST_ARM) + +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_ARM +#define elf_check_arch(x) ((x) == EM_ARM) +#define ELF_USES_RELOC + +#elif defined(HOST_M68K) + +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_68K +#define elf_check_arch(x) ((x) == EM_68K) +#define ELF_USES_RELOCA + +#else +#error unsupported CPU - please update the code +#endif + +#include "elf.h" + +#if ELF_CLASS == ELFCLASS32 +typedef int32_t host_long; +typedef uint32_t host_ulong; +#define swabls(x) swab32s(x) +#else +typedef int64_t host_long; +typedef uint64_t host_ulong; +#define swabls(x) swab64s(x) +#endif + +#ifdef ELF_USES_RELOCA +#define SHT_RELOC SHT_RELA +#else +#define SHT_RELOC SHT_REL +#endif + +#define EXE_RELOC ELF_RELOC +#define EXE_SYM ElfW(Sym) + +#endif /* CONFIG_FORMAT_ELF */ + +#ifdef CONFIG_FORMAT_COFF + +#include "a.out.h" + +typedef int32_t host_long; +typedef uint32_t host_ulong; + +#define FILENAMELEN 256 + +typedef struct coff_sym { + struct external_syment *st_syment; + char st_name[FILENAMELEN]; + uint32_t st_value; + int st_size; + uint8_t st_type; + uint8_t st_shndx; +} coff_Sym; + +typedef struct coff_rel { + struct external_reloc *r_reloc; + int r_offset; + uint8_t r_type; +} coff_Rel; + +#define EXE_RELOC struct coff_rel +#define EXE_SYM struct coff_sym + +#endif /* CONFIG_FORMAT_COFF */ + +#ifdef CONFIG_FORMAT_MACH + +#include +#include +#include +#include + +# define check_mach_header(x) (x.magic == MH_MAGIC) +typedef int32_t host_long; +typedef uint32_t host_ulong; + +struct nlist_extended +{ + union { + char *n_name; + long n_strx; + } n_un; + unsigned char n_type; + unsigned char n_sect; + short st_desc; + unsigned long st_value; + unsigned long st_size; +}; + +#define EXE_RELOC struct relocation_info +#define EXE_SYM struct nlist_extended + +#endif /* CONFIG_FORMAT_MACH */ + +#include "bswap.h" + +enum { + OUT_GEN_OP, + OUT_CODE, + OUT_INDEX_OP, +}; + +/* all dynamically generated functions begin with this code */ +#define OP_PREFIX "op_" + +int do_swap; + +void __attribute__((noreturn)) __attribute__((format (printf, 1, 2))) error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "dyngen: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} + +void *load_data(int fd, long offset, unsigned int size) +{ + char *data; + + data = malloc(size); + if (!data) + return NULL; + lseek(fd, offset, SEEK_SET); + if (read(fd, data, size) != size) { + free(data); + return NULL; + } + return data; +} + +int strstart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (*p != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +void pstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + if (buf_size <= 0) + return; + + for(;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = c; + } + *q = '\0'; +} + +void swab16s(uint16_t *p) +{ + *p = bswap16(*p); +} + +void swab32s(uint32_t *p) +{ + *p = bswap32(*p); +} + +void swab64s(uint64_t *p) +{ + *p = bswap64(*p); +} + +uint16_t get16(uint16_t *p) +{ + uint16_t val; + val = *p; + if (do_swap) + val = bswap16(val); + return val; +} + +uint32_t get32(uint32_t *p) +{ + uint32_t val; + val = *p; + if (do_swap) + val = bswap32(val); + return val; +} + +void put16(uint16_t *p, uint16_t val) +{ + if (do_swap) + val = bswap16(val); + *p = val; +} + +void put32(uint32_t *p, uint32_t val) +{ + if (do_swap) + val = bswap32(val); + *p = val; +} + +/* executable information */ +EXE_SYM *symtab; +int nb_syms; +int text_shndx; +uint8_t *text; +EXE_RELOC *relocs; +int nb_relocs; + +#ifdef CONFIG_FORMAT_ELF + +/* ELF file info */ +struct elf_shdr *shdr; +uint8_t **sdata; +struct elfhdr ehdr; +char *strtab; + +int elf_must_swap(struct elfhdr *h) +{ + union { + uint32_t i; + uint8_t b[4]; + } swaptest; + + swaptest.i = 1; + return (h->e_ident[EI_DATA] == ELFDATA2MSB) != + (swaptest.b[0] == 0); +} + +void elf_swap_ehdr(struct elfhdr *h) +{ + swab16s(&h->e_type); /* Object file type */ + swab16s(&h-> e_machine); /* Architecture */ + swab32s(&h-> e_version); /* Object file version */ + swabls(&h-> e_entry); /* Entry point virtual address */ + swabls(&h-> e_phoff); /* Program header table file offset */ + swabls(&h-> e_shoff); /* Section header table file offset */ + swab32s(&h-> e_flags); /* Processor-specific flags */ + swab16s(&h-> e_ehsize); /* ELF header size in bytes */ + swab16s(&h-> e_phentsize); /* Program header table entry size */ + swab16s(&h-> e_phnum); /* Program header table entry count */ + swab16s(&h-> e_shentsize); /* Section header table entry size */ + swab16s(&h-> e_shnum); /* Section header table entry count */ + swab16s(&h-> e_shstrndx); /* Section header string table index */ +} + +void elf_swap_shdr(struct elf_shdr *h) +{ + swab32s(&h-> sh_name); /* Section name (string tbl index) */ + swab32s(&h-> sh_type); /* Section type */ + swabls(&h-> sh_flags); /* Section flags */ + swabls(&h-> sh_addr); /* Section virtual addr at execution */ + swabls(&h-> sh_offset); /* Section file offset */ + swabls(&h-> sh_size); /* Section size in bytes */ + swab32s(&h-> sh_link); /* Link to another section */ + swab32s(&h-> sh_info); /* Additional section information */ + swabls(&h-> sh_addralign); /* Section alignment */ + swabls(&h-> sh_entsize); /* Entry size if section holds table */ +} + +void elf_swap_phdr(struct elf_phdr *h) +{ + swab32s(&h->p_type); /* Segment type */ + swabls(&h->p_offset); /* Segment file offset */ + swabls(&h->p_vaddr); /* Segment virtual address */ + swabls(&h->p_paddr); /* Segment physical address */ + swabls(&h->p_filesz); /* Segment size in file */ + swabls(&h->p_memsz); /* Segment size in memory */ + swab32s(&h->p_flags); /* Segment flags */ + swabls(&h->p_align); /* Segment alignment */ +} + +void elf_swap_rel(ELF_RELOC *rel) +{ + swabls(&rel->r_offset); + swabls(&rel->r_info); +#ifdef ELF_USES_RELOCA + swabls(&rel->r_addend); +#endif +} + +struct elf_shdr *find_elf_section(struct elf_shdr *shdr, int shnum, const char *shstr, + const char *name) +{ + int i; + const char *shname; + struct elf_shdr *sec; + + for(i = 0; i < shnum; i++) { + sec = &shdr[i]; + if (!sec->sh_name) + continue; + shname = shstr + sec->sh_name; + if (!strcmp(shname, name)) + return sec; + } + return NULL; +} + +int find_reloc(int sh_index) +{ + struct elf_shdr *sec; + int i; + + for(i = 0; i < ehdr.e_shnum; i++) { + sec = &shdr[i]; + if (sec->sh_type == SHT_RELOC && sec->sh_info == sh_index) + return i; + } + return 0; +} + +static host_ulong get_rel_offset(EXE_RELOC *rel) +{ + return rel->r_offset; +} + +static char *get_rel_sym_name(EXE_RELOC *rel) +{ + return strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name; +} + +static char *get_sym_name(EXE_SYM *sym) +{ + return strtab + sym->st_name; +} + +/* load an elf object file */ +int load_object(const char *filename) +{ + int fd; + struct elf_shdr *sec, *symtab_sec, *strtab_sec, *text_sec; + int i, j; + ElfW(Sym) *sym; + char *shstr; + ELF_RELOC *rel; + + fd = open(filename, O_RDONLY); + if (fd < 0) + error("can't open file '%s'", filename); + + /* Read ELF header. */ + if (read(fd, &ehdr, sizeof (ehdr)) != sizeof (ehdr)) + error("unable to read file header"); + + /* Check ELF identification. */ + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 + || ehdr.e_ident[EI_MAG1] != ELFMAG1 + || ehdr.e_ident[EI_MAG2] != ELFMAG2 + || ehdr.e_ident[EI_MAG3] != ELFMAG3 + || ehdr.e_ident[EI_VERSION] != EV_CURRENT) { + error("bad ELF header"); + } + + do_swap = elf_must_swap(&ehdr); + if (do_swap) + elf_swap_ehdr(&ehdr); + if (ehdr.e_ident[EI_CLASS] != ELF_CLASS) + error("Unsupported ELF class"); + if (ehdr.e_type != ET_REL) + error("ELF object file expected"); + if (ehdr.e_version != EV_CURRENT) + error("Invalid ELF version"); + if (!elf_check_arch(ehdr.e_machine)) + error("Unsupported CPU (e_machine=%d)", ehdr.e_machine); + + /* read section headers */ + shdr = load_data(fd, ehdr.e_shoff, ehdr.e_shnum * sizeof(struct elf_shdr)); + if (do_swap) { + for(i = 0; i < ehdr.e_shnum; i++) { + elf_swap_shdr(&shdr[i]); + } + } + + /* read all section data */ + sdata = malloc(sizeof(void *) * ehdr.e_shnum); + memset(sdata, 0, sizeof(void *) * ehdr.e_shnum); + + for(i = 0;i < ehdr.e_shnum; i++) { + sec = &shdr[i]; + if (sec->sh_type != SHT_NOBITS) + sdata[i] = load_data(fd, sec->sh_offset, sec->sh_size); + } + + sec = &shdr[ehdr.e_shstrndx]; + shstr = sdata[ehdr.e_shstrndx]; + + /* swap relocations */ + for(i = 0; i < ehdr.e_shnum; i++) { + sec = &shdr[i]; + if (sec->sh_type == SHT_RELOC) { + nb_relocs = sec->sh_size / sec->sh_entsize; + if (do_swap) { + for(j = 0, rel = (ELF_RELOC *)sdata[i]; j < nb_relocs; j++, rel++) + elf_swap_rel(rel); + } + } + } + /* text section */ + + text_sec = find_elf_section(shdr, ehdr.e_shnum, shstr, ".text"); + if (!text_sec) + error("could not find .text section"); + text_shndx = text_sec - shdr; + text = sdata[text_shndx]; + + /* find text relocations, if any */ + relocs = NULL; + nb_relocs = 0; + i = find_reloc(text_shndx); + if (i != 0) { + relocs = (ELF_RELOC *)sdata[i]; + nb_relocs = shdr[i].sh_size / shdr[i].sh_entsize; + } + + symtab_sec = find_elf_section(shdr, ehdr.e_shnum, shstr, ".symtab"); + if (!symtab_sec) + error("could not find .symtab section"); + strtab_sec = &shdr[symtab_sec->sh_link]; + + symtab = (ElfW(Sym) *)sdata[symtab_sec - shdr]; + strtab = sdata[symtab_sec->sh_link]; + + nb_syms = symtab_sec->sh_size / sizeof(ElfW(Sym)); + if (do_swap) { + for(i = 0, sym = symtab; i < nb_syms; i++, sym++) { + swab32s(&sym->st_name); + swabls(&sym->st_value); + swabls(&sym->st_size); + swab16s(&sym->st_shndx); + } + } + close(fd); + return 0; +} + +#endif /* CONFIG_FORMAT_ELF */ + +#ifdef CONFIG_FORMAT_COFF + +/* COFF file info */ +struct external_scnhdr *shdr; +uint8_t **sdata; +struct external_filehdr fhdr; +struct external_syment *coff_symtab; +char *strtab; +int coff_text_shndx, coff_data_shndx; + +int data_shndx; + +#define STRTAB_SIZE 4 + +#define DIR32 0x06 +#define DISP32 0x14 + +#define T_FUNCTION 0x20 +#define C_EXTERNAL 2 + +void sym_ent_name(struct external_syment *ext_sym, EXE_SYM *sym) +{ + char *q; + int c, i, len; + + if (ext_sym->e.e.e_zeroes != 0) { + q = sym->st_name; + for(i = 0; i < 8; i++) { + c = ext_sym->e.e_name[i]; + if (c == '\0') + break; + *q++ = c; + } + *q = '\0'; + } else { + pstrcpy(sym->st_name, sizeof(sym->st_name), strtab + ext_sym->e.e.e_offset); + } + + /* now convert the name to a C name (suppress the leading '_') */ + if (sym->st_name[0] == '_') { + len = strlen(sym->st_name); + memmove(sym->st_name, sym->st_name + 1, len - 1); + sym->st_name[len - 1] = '\0'; + } +} + +char *name_for_dotdata(struct coff_rel *rel) +{ + int i; + struct coff_sym *sym; + uint32_t text_data; + + text_data = *(uint32_t *)(text + rel->r_offset); + + for (i = 0, sym = symtab; i < nb_syms; i++, sym++) { + if (sym->st_syment->e_scnum == data_shndx && + text_data >= sym->st_value && + text_data < sym->st_value + sym->st_size) { + + return sym->st_name; + + } + } + return NULL; +} + +static char *get_sym_name(EXE_SYM *sym) +{ + return sym->st_name; +} + +static char *get_rel_sym_name(EXE_RELOC *rel) +{ + char *name; + name = get_sym_name(symtab + *(uint32_t *)(rel->r_reloc->r_symndx)); + if (!strcmp(name, ".data")) + name = name_for_dotdata(rel); + if (name[0] == '.') + return NULL; + return name; +} + +static host_ulong get_rel_offset(EXE_RELOC *rel) +{ + return rel->r_offset; +} + +struct external_scnhdr *find_coff_section(struct external_scnhdr *shdr, int shnum, const char *name) +{ + int i; + const char *shname; + struct external_scnhdr *sec; + + for(i = 0; i < shnum; i++) { + sec = &shdr[i]; + if (!sec->s_name) + continue; + shname = sec->s_name; + if (!strcmp(shname, name)) + return sec; + } + return NULL; +} + +/* load a coff object file */ +int load_object(const char *filename) +{ + int fd; + struct external_scnhdr *sec, *text_sec, *data_sec; + int i; + struct external_syment *ext_sym; + struct external_reloc *coff_relocs; + struct external_reloc *ext_rel; + uint32_t *n_strtab; + EXE_SYM *sym; + EXE_RELOC *rel; + + fd = open(filename, O_RDONLY +#ifdef _WIN32 + | O_BINARY +#endif + ); + if (fd < 0) + error("can't open file '%s'", filename); + + /* Read COFF header. */ + if (read(fd, &fhdr, sizeof (fhdr)) != sizeof (fhdr)) + error("unable to read file header"); + + /* Check COFF identification. */ + if (fhdr.f_magic != I386MAGIC) { + error("bad COFF header"); + } + do_swap = 0; + + /* read section headers */ + shdr = load_data(fd, sizeof(struct external_filehdr) + fhdr.f_opthdr, fhdr.f_nscns * sizeof(struct external_scnhdr)); + + /* read all section data */ + sdata = malloc(sizeof(void *) * fhdr.f_nscns); + memset(sdata, 0, sizeof(void *) * fhdr.f_nscns); + + const char *p; + for(i = 0;i < fhdr.f_nscns; i++) { + sec = &shdr[i]; + if (!strstart(sec->s_name, ".bss", &p)) + sdata[i] = load_data(fd, sec->s_scnptr, sec->s_size); + } + + + /* text section */ + text_sec = find_coff_section(shdr, fhdr.f_nscns, ".text"); + if (!text_sec) + error("could not find .text section"); + coff_text_shndx = text_sec - shdr; + text = sdata[coff_text_shndx]; + + /* data section */ + data_sec = find_coff_section(shdr, fhdr.f_nscns, ".data"); + if (!data_sec) + error("could not find .data section"); + coff_data_shndx = data_sec - shdr; + + coff_symtab = load_data(fd, fhdr.f_symptr, fhdr.f_nsyms*SYMESZ); + for (i = 0, ext_sym = coff_symtab; i < nb_syms; i++, ext_sym++) { + for(i=0;i<8;i++) + printf(" %02x", ((uint8_t *)ext_sym->e.e_name)[i]); + printf("\n"); + } + + + n_strtab = load_data(fd, (fhdr.f_symptr + fhdr.f_nsyms*SYMESZ), STRTAB_SIZE); + strtab = load_data(fd, (fhdr.f_symptr + fhdr.f_nsyms*SYMESZ), *n_strtab); + + nb_syms = fhdr.f_nsyms; + + for (i = 0, ext_sym = coff_symtab; i < nb_syms; i++, ext_sym++) { + if (strstart(ext_sym->e.e_name, ".text", NULL)) + text_shndx = ext_sym->e_scnum; + if (strstart(ext_sym->e.e_name, ".data", NULL)) + data_shndx = ext_sym->e_scnum; + } + + /* set coff symbol */ + symtab = malloc(sizeof(struct coff_sym) * nb_syms); + + int aux_size, j; + for (i = 0, ext_sym = coff_symtab, sym = symtab; i < nb_syms; i++, ext_sym++, sym++) { + memset(sym, 0, sizeof(*sym)); + sym->st_syment = ext_sym; + sym_ent_name(ext_sym, sym); + sym->st_value = ext_sym->e_value; + + aux_size = *(int8_t *)ext_sym->e_numaux; + if (ext_sym->e_scnum == text_shndx && ext_sym->e_type == T_FUNCTION) { + for (j = aux_size + 1; j < nb_syms - i; j++) { + if ((ext_sym + j)->e_scnum == text_shndx && + (ext_sym + j)->e_type == T_FUNCTION ){ + sym->st_size = (ext_sym + j)->e_value - ext_sym->e_value; + break; + } else if (j == nb_syms - i - 1) { + sec = &shdr[coff_text_shndx]; + sym->st_size = sec->s_size - ext_sym->e_value; + break; + } + } + } else if (ext_sym->e_scnum == data_shndx && *(uint8_t *)ext_sym->e_sclass == C_EXTERNAL) { + for (j = aux_size + 1; j < nb_syms - i; j++) { + if ((ext_sym + j)->e_scnum == data_shndx) { + sym->st_size = (ext_sym + j)->e_value - ext_sym->e_value; + break; + } else if (j == nb_syms - i - 1) { + sec = &shdr[coff_data_shndx]; + sym->st_size = sec->s_size - ext_sym->e_value; + break; + } + } + } else { + sym->st_size = 0; + } + + sym->st_type = ext_sym->e_type; + sym->st_shndx = ext_sym->e_scnum; + } + + + /* find text relocations, if any */ + sec = &shdr[coff_text_shndx]; + coff_relocs = load_data(fd, sec->s_relptr, sec->s_nreloc*RELSZ); + nb_relocs = sec->s_nreloc; + + /* set coff relocation */ + relocs = malloc(sizeof(struct coff_rel) * nb_relocs); + for (i = 0, ext_rel = coff_relocs, rel = relocs; i < nb_relocs; + i++, ext_rel++, rel++) { + memset(rel, 0, sizeof(*rel)); + rel->r_reloc = ext_rel; + rel->r_offset = *(uint32_t *)ext_rel->r_vaddr; + rel->r_type = *(uint16_t *)ext_rel->r_type; + } + return 0; +} + +#endif /* CONFIG_FORMAT_COFF */ + +#ifdef CONFIG_FORMAT_MACH + +/* File Header */ +struct mach_header mach_hdr; + +/* commands */ +struct segment_command *segment = 0; +struct dysymtab_command *dysymtabcmd = 0; +struct symtab_command *symtabcmd = 0; + +/* section */ +struct section *section_hdr; +struct section *text_sec_hdr; +uint8_t **sdata; + +/* relocs */ +struct relocation_info *relocs; + +/* symbols */ +EXE_SYM *symtab; +struct nlist *symtab_std; +char *strtab; + +/* indirect symbols */ +uint32_t *tocdylib; + +/* Utility functions */ + +static inline char *find_str_by_index(int index) +{ + return strtab+index; +} + +/* Used by dyngen common code */ +static char *get_sym_name(EXE_SYM *sym) +{ + char *name = find_str_by_index(sym->n_un.n_strx); + + if ( sym->n_type & N_STAB ) /* Debug symbols are ignored */ + return "debug"; + + if(!name) + return name; + if(name[0]=='_') + return name + 1; + else + return name; +} + +/* find a section index given its segname, sectname */ +static int find_mach_sec_index(struct section *section_hdr, int shnum, const char *segname, + const char *sectname) +{ + int i; + struct section *sec = section_hdr; + + for(i = 0; i < shnum; i++, sec++) { + if (!sec->segname || !sec->sectname) + continue; + if (!strcmp(sec->sectname, sectname) && !strcmp(sec->segname, segname)) + return i; + } + return -1; +} + +/* find a section header given its segname, sectname */ +struct section *find_mach_sec_hdr(struct section *section_hdr, int shnum, const char *segname, + const char *sectname) +{ + int index = find_mach_sec_index(section_hdr, shnum, segname, sectname); + if(index == -1) + return NULL; + return section_hdr+index; +} + + +static inline void fetch_next_pair_value(struct relocation_info * rel, unsigned int *value) +{ + struct scattered_relocation_info * scarel; + + if(R_SCATTERED & rel->r_address) { + scarel = (struct scattered_relocation_info*)rel; + if(scarel->r_type != PPC_RELOC_PAIR) + error("fetch_next_pair_value: looking for a pair which was not found (1)"); + *value = scarel->r_value; + } else { + if(rel->r_type != PPC_RELOC_PAIR) + error("fetch_next_pair_value: looking for a pair which was not found (2)"); + *value = rel->r_address; + } +} + +/* find a sym name given its value, in a section number */ +static const char * find_sym_with_value_and_sec_number( int value, int sectnum, int * offset ) +{ + int i, ret = -1; + + for( i = 0 ; i < nb_syms; i++ ) + { + if( !(symtab[i].n_type & N_STAB) && (symtab[i].n_type & N_SECT) && + (symtab[i].n_sect == sectnum) && (symtab[i].st_value <= value) ) + { + if( (ret<0) || (symtab[i].st_value >= symtab[ret].st_value) ) + ret = i; + } + } + if( ret < 0 ) { + *offset = 0; + return 0; + } else { + *offset = value - symtab[ret].st_value; + return get_sym_name(&symtab[ret]); + } +} + +/* + * Find symbol name given a (virtual) address, and a section which is of type + * S_NON_LAZY_SYMBOL_POINTERS or S_LAZY_SYMBOL_POINTERS or S_SYMBOL_STUBS + */ +static const char * find_reloc_name_in_sec_ptr(int address, struct section * sec_hdr) +{ + unsigned int tocindex, symindex, size; + const char *name = 0; + + /* Sanity check */ + if(!( address >= sec_hdr->addr && address < (sec_hdr->addr + sec_hdr->size) ) ) + return (char*)0; + + if( sec_hdr->flags & S_SYMBOL_STUBS ){ + size = sec_hdr->reserved2; + if(size == 0) + error("size = 0"); + + } + else if( sec_hdr->flags & S_LAZY_SYMBOL_POINTERS || + sec_hdr->flags & S_NON_LAZY_SYMBOL_POINTERS) + size = sizeof(unsigned long); + else + return 0; + + /* Compute our index in toc */ + tocindex = (address - sec_hdr->addr)/size; + symindex = tocdylib[sec_hdr->reserved1 + tocindex]; + + name = get_sym_name(&symtab[symindex]); + + return name; +} + +static const char * find_reloc_name_given_its_address(int address) +{ + unsigned int i; + for(i = 0; i < segment->nsects ; i++) + { + const char * name = find_reloc_name_in_sec_ptr(address, §ion_hdr[i]); + if((long)name != -1) + return name; + } + return 0; +} + +static const char * get_reloc_name(EXE_RELOC * rel, int * sslide) +{ + char * name = 0; + struct scattered_relocation_info * sca_rel = (struct scattered_relocation_info*)rel; + int sectnum = rel->r_symbolnum; + int sectoffset; + int other_half=0; + + /* init the slide value */ + *sslide = 0; + + if(R_SCATTERED & rel->r_address) + return (char *)find_reloc_name_given_its_address(sca_rel->r_value); + + if(rel->r_extern) + { + /* ignore debug sym */ + if ( symtab[rel->r_symbolnum].n_type & N_STAB ) + return 0; + return get_sym_name(&symtab[rel->r_symbolnum]); + } + + /* Intruction contains an offset to the symbols pointed to, in the rel->r_symbolnum section */ + sectoffset = *(uint32_t *)(text + rel->r_address) & 0xffff; + + if(sectnum==0xffffff) + return 0; + + /* Sanity Check */ + if(sectnum > segment->nsects) + error("sectnum > segment->nsects"); + + switch(rel->r_type) + { + case PPC_RELOC_LO16: fetch_next_pair_value(rel+1, &other_half); sectoffset |= (other_half << 16); + break; + case PPC_RELOC_HI16: fetch_next_pair_value(rel+1, &other_half); sectoffset = (sectoffset << 16) | (uint16_t)(other_half & 0xffff); + break; + case PPC_RELOC_HA16: fetch_next_pair_value(rel+1, &other_half); sectoffset = (sectoffset << 16) + (int16_t)(other_half & 0xffff); + break; + case PPC_RELOC_BR24: + sectoffset = ( *(uint32_t *)(text + rel->r_address) & 0x03fffffc ); + if (sectoffset & 0x02000000) sectoffset |= 0xfc000000; + break; + default: + error("switch(rel->type) not found"); + } + + if(rel->r_pcrel) + sectoffset += rel->r_address; + + if (rel->r_type == PPC_RELOC_BR24) + name = (char *)find_reloc_name_in_sec_ptr((int)sectoffset, §ion_hdr[sectnum-1]); + + /* search it in the full symbol list, if not found */ + if(!name) + name = (char *)find_sym_with_value_and_sec_number(sectoffset, sectnum, sslide); + + return name; +} + +/* Used by dyngen common code */ +static const char * get_rel_sym_name(EXE_RELOC * rel) +{ + int sslide; + return get_reloc_name( rel, &sslide); +} + +/* Used by dyngen common code */ +static host_ulong get_rel_offset(EXE_RELOC *rel) +{ + struct scattered_relocation_info * sca_rel = (struct scattered_relocation_info*)rel; + if(R_SCATTERED & rel->r_address) + return sca_rel->r_address; + else + return rel->r_address; +} + +/* load a mach-o object file */ +int load_object(const char *filename) +{ + int fd; + unsigned int offset_to_segment = 0; + unsigned int offset_to_dysymtab = 0; + unsigned int offset_to_symtab = 0; + struct load_command lc; + unsigned int i, j; + EXE_SYM *sym; + struct nlist *syment; + + fd = open(filename, O_RDONLY); + if (fd < 0) + error("can't open file '%s'", filename); + + /* Read Mach header. */ + if (read(fd, &mach_hdr, sizeof (mach_hdr)) != sizeof (mach_hdr)) + error("unable to read file header"); + + /* Check Mach identification. */ + if (!check_mach_header(mach_hdr)) { + error("bad Mach header"); + } + + if (mach_hdr.cputype != CPU_TYPE_POWERPC) + error("Unsupported CPU"); + + if (mach_hdr.filetype != MH_OBJECT) + error("Unsupported Mach Object"); + + /* read segment headers */ + for(i=0, j=sizeof(mach_hdr); insects * sizeof(struct section)); + + /* read all section data */ + sdata = (uint8_t **)malloc(sizeof(void *) * segment->nsects); + memset(sdata, 0, sizeof(void *) * segment->nsects); + + /* Load the data in section data */ + for(i = 0; i < segment->nsects; i++) { + sdata[i] = load_data(fd, section_hdr[i].offset, section_hdr[i].size); + } + + /* text section */ + text_sec_hdr = find_mach_sec_hdr(section_hdr, segment->nsects, SEG_TEXT, SECT_TEXT); + i = find_mach_sec_index(section_hdr, segment->nsects, SEG_TEXT, SECT_TEXT); + if (i == -1 || !text_sec_hdr) + error("could not find __TEXT,__text section"); + text = sdata[i]; + + /* Make sure dysym was loaded */ + if(!(int)dysymtabcmd) + error("could not find __DYSYMTAB segment"); + + /* read the table of content of the indirect sym */ + tocdylib = load_data( fd, dysymtabcmd->indirectsymoff, dysymtabcmd->nindirectsyms * sizeof(uint32_t) ); + + /* Make sure symtab was loaded */ + if(!(int)symtabcmd) + error("could not find __SYMTAB segment"); + nb_syms = symtabcmd->nsyms; + + symtab_std = load_data(fd, symtabcmd->symoff, symtabcmd->nsyms * sizeof(struct nlist)); + strtab = load_data(fd, symtabcmd->stroff, symtabcmd->strsize); + + symtab = malloc(sizeof(EXE_SYM) * nb_syms); + + /* Now transform the symtab, to an extended version, with the sym size, and the C name */ + for(i = 0, sym = symtab, syment = symtab_std; i < nb_syms; i++, sym++, syment++) { + struct nlist *sym_follow, *sym_next = 0; + unsigned int j; + memset(sym, 0, sizeof(*sym)); + + if ( syment->n_type & N_STAB ) /* Debug symbols are skipped */ + continue; + + memcpy(sym, syment, sizeof(*syment)); + + /* Find the following symbol in order to get the current symbol size */ + for(j = 0, sym_follow = symtab_std; j < nb_syms; j++, sym_follow++) { + if ( sym_follow->n_sect != 1 || sym_follow->n_type & N_STAB || !(sym_follow->n_value > sym->st_value)) + continue; + if(!sym_next) { + sym_next = sym_follow; + continue; + } + if(!(sym_next->n_value > sym_follow->n_value)) + continue; + sym_next = sym_follow; + } + if(sym_next) + sym->st_size = sym_next->n_value - sym->st_value; + else + sym->st_size = text_sec_hdr->size - sym->st_value; + } + + /* Find Reloc */ + relocs = load_data(fd, text_sec_hdr->reloff, text_sec_hdr->nreloc * sizeof(struct relocation_info)); + nb_relocs = text_sec_hdr->nreloc; + + close(fd); + return 0; +} + +#endif /* CONFIG_FORMAT_MACH */ + +void get_reloc_expr(char *name, int name_size, const char *sym_name) +{ + const char *p; + + if (strstart(sym_name, "__op_param", &p)) { + snprintf(name, name_size, "param%s", p); + } else if (strstart(sym_name, "__op_gen_label", &p)) { + snprintf(name, name_size, "gen_labels[param%s]", p); + } else { +#ifdef HOST_SPARC + if (sym_name[0] == '.') + snprintf(name, sizeof(name), + "(long)(&__dot_%s)", + sym_name + 1); + else +#endif + snprintf(name, name_size, "(long)(&%s)", sym_name); + } +} + +#ifdef HOST_IA64 + +#define PLT_ENTRY_SIZE 16 /* 1 bundle containing "brl" */ + +struct plt_entry { + struct plt_entry *next; + const char *name; + unsigned long addend; +} *plt_list; + +static int +get_plt_index (const char *name, unsigned long addend) +{ + struct plt_entry *plt, *prev= NULL; + int index = 0; + + /* see if we already have an entry for this target: */ + for (plt = plt_list; plt; ++index, prev = plt, plt = plt->next) + if (strcmp(plt->name, name) == 0 && plt->addend == addend) + return index; + + /* nope; create a new PLT entry: */ + + plt = malloc(sizeof(*plt)); + if (!plt) { + perror("malloc"); + exit(1); + } + memset(plt, 0, sizeof(*plt)); + plt->name = strdup(name); + plt->addend = addend; + + /* append to plt-list: */ + if (prev) + prev->next = plt; + else + plt_list = plt; + return index; +} + +#endif + +#ifdef HOST_ARM + +int arm_emit_ldr_info(const char *name, unsigned long start_offset, + FILE *outfile, uint8_t *p_start, uint8_t *p_end, + ELF_RELOC *relocs, int nb_relocs) +{ + uint8_t *p; + uint32_t insn; + int offset, min_offset, pc_offset, data_size; + uint8_t data_allocated[1024]; + unsigned int data_index; + + memset(data_allocated, 0, sizeof(data_allocated)); + + p = p_start; + min_offset = p_end - p_start; + while (p < p_start + min_offset) { + insn = get32((uint32_t *)p); + if ((insn & 0x0d5f0000) == 0x051f0000) { + /* ldr reg, [pc, #im] */ + offset = insn & 0xfff; + if (!(insn & 0x00800000)) + offset = -offset; + if ((offset & 3) !=0) + error("%s:%04x: ldr pc offset must be 32 bit aligned", + name, start_offset + p - p_start); + pc_offset = p - p_start + offset + 8; + if (pc_offset <= (p - p_start) || + pc_offset >= (p_end - p_start)) + error("%s:%04x: ldr pc offset must point inside the function code", + name, start_offset + p - p_start); + if (pc_offset < min_offset) + min_offset = pc_offset; + if (outfile) { + /* ldr position */ + fprintf(outfile, " arm_ldr_ptr->ptr = gen_code_ptr + %d;\n", + p - p_start); + /* ldr data index */ + data_index = ((p_end - p_start) - pc_offset - 4) >> 2; + fprintf(outfile, " arm_ldr_ptr->data_ptr = arm_data_ptr + %d;\n", + data_index); + fprintf(outfile, " arm_ldr_ptr++;\n"); + if (data_index >= sizeof(data_allocated)) + error("%s: too many data", name); + if (!data_allocated[data_index]) { + ELF_RELOC *rel; + int i, addend, type; + const char *sym_name, *p; + char relname[1024]; + + data_allocated[data_index] = 1; + + /* data value */ + addend = get32((uint32_t *)(p_start + pc_offset)); + relname[0] = '\0'; + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + if (rel->r_offset == (pc_offset + start_offset)) { + sym_name = get_rel_sym_name(rel); + /* the compiler leave some unnecessary references to the code */ + get_reloc_expr(relname, sizeof(relname), sym_name); + type = ELF32_R_TYPE(rel->r_info); + if (type != R_ARM_ABS32) + error("%s: unsupported data relocation", name); + break; + } + } + fprintf(outfile, " arm_data_ptr[%d] = 0x%x", + data_index, addend); + if (relname[0] != '\0') + fprintf(outfile, " + %s", relname); + fprintf(outfile, ";\n"); + } + } + } + p += 4; + } + data_size = (p_end - p_start) - min_offset; + if (data_size > 0 && outfile) { + fprintf(outfile, " arm_data_ptr += %d;\n", data_size >> 2); + } + + /* the last instruction must be a mov pc, lr */ + if (p == p_start) + goto arm_ret_error; + p -= 4; + insn = get32((uint32_t *)p); + if ((insn & 0xffff0000) != 0xe91b0000) { + arm_ret_error: + if (!outfile) + printf("%s: invalid epilog\n", name); + } + return p - p_start; +} +#endif + + +#define MAX_ARGS 3 + +/* generate op code */ +void gen_code(const char *name, host_ulong offset, host_ulong size, + FILE *outfile, int gen_switch) +{ + int copy_size = 0; + uint8_t *p_start, *p_end; + host_ulong start_offset; + int nb_args, i, n; + uint8_t args_present[MAX_ARGS]; + const char *sym_name, *p; + EXE_RELOC *rel; + + /* Compute exact size excluding prologue and epilogue instructions. + * Increment start_offset to skip epilogue instructions, then compute + * copy_size the indicate the size of the remaining instructions (in + * bytes). + */ + p_start = text + offset; + p_end = p_start + size; + start_offset = offset; +#if defined(HOST_I386) || defined(HOST_X86_64) +#ifdef CONFIG_FORMAT_COFF + { + uint8_t *p; + p = p_end - 1; + if (p == p_start) + error("empty code for %s", name); + while (*p != 0xc3) { + p--; + if (p <= p_start) + error("ret or jmp expected at the end of %s", name); + } + copy_size = p - p_start; + } +#else + { + int len; + len = p_end - p_start; + if (len == 0) + error("empty code for %s", name); + if (p_end[-1] == 0xc3) { + len--; + } else { + error("ret or jmp expected at the end of %s", name); + } + copy_size = len; + } +#endif +#elif defined(HOST_PPC) + { + uint8_t *p; + p = (void *)(p_end - 4); + if (p == p_start) + error("empty code for %s", name); + if (get32((uint32_t *)p) != 0x4e800020) + error("blr expected at the end of %s", name); + copy_size = p - p_start; + } +#elif defined(HOST_S390) + { + uint8_t *p; + p = (void *)(p_end - 2); + if (p == p_start) + error("empty code for %s", name); + if (get16((uint16_t *)p) != 0x07fe && get16((uint16_t *)p) != 0x07f4) + error("br %%r14 expected at the end of %s", name); + copy_size = p - p_start; + } +#elif defined(HOST_ALPHA) + { + uint8_t *p; + p = p_end - 4; +#if 0 + /* XXX: check why it occurs */ + if (p == p_start) + error("empty code for %s", name); +#endif + if (get32((uint32_t *)p) != 0x6bfa8001) + error("ret expected at the end of %s", name); + copy_size = p - p_start; + } +#elif defined(HOST_IA64) + { + uint8_t *p; + p = (void *)(p_end - 4); + if (p == p_start) + error("empty code for %s", name); + /* br.ret.sptk.many b0;; */ + /* 08 00 84 00 */ + if (get32((uint32_t *)p) != 0x00840008) + error("br.ret.sptk.many b0;; expected at the end of %s", name); + copy_size = p_end - p_start; + } +#elif defined(HOST_SPARC) + { + uint32_t start_insn, end_insn1, end_insn2; + uint8_t *p; + p = (void *)(p_end - 8); + if (p <= p_start) + error("empty code for %s", name); + start_insn = get32((uint32_t *)(p_start + 0x0)); + end_insn1 = get32((uint32_t *)(p + 0x0)); + end_insn2 = get32((uint32_t *)(p + 0x4)); + if ((start_insn & ~0x1fff) == 0x9de3a000) { + p_start += 0x4; + start_offset += 0x4; + if ((int)(start_insn | ~0x1fff) < -128) + error("Found bogus save at the start of %s", name); + if (end_insn1 != 0x81c7e008 || end_insn2 != 0x81e80000) + error("ret; restore; not found at end of %s", name); + } else { + error("No save at the beginning of %s", name); + } +#if 0 + /* Skip a preceeding nop, if present. */ + if (p > p_start) { + skip_insn = get32((uint32_t *)(p - 0x4)); + if (skip_insn == 0x01000000) + p -= 4; + } +#endif + copy_size = p - p_start; + } +#elif defined(HOST_SPARC64) + { + uint32_t start_insn, end_insn1, end_insn2, skip_insn; + uint8_t *p; + p = (void *)(p_end - 8); + if (p <= p_start) + error("empty code for %s", name); + start_insn = get32((uint32_t *)(p_start + 0x0)); + end_insn1 = get32((uint32_t *)(p + 0x0)); + end_insn2 = get32((uint32_t *)(p + 0x4)); + if ((start_insn & ~0x1fff) == 0x9de3a000) { + p_start += 0x4; + start_offset += 0x4; + if ((int)(start_insn | ~0x1fff) < -256) + error("Found bogus save at the start of %s", name); + if (end_insn1 != 0x81c7e008 || end_insn2 != 0x81e80000) + error("ret; restore; not found at end of %s", name); + } else { + error("No save at the beginning of %s", name); + } + + /* Skip a preceeding nop, if present. */ + if (p > p_start) { + skip_insn = get32((uint32_t *)(p - 0x4)); + if (skip_insn == 0x01000000) + p -= 4; + } + + copy_size = p - p_start; + } +#elif defined(HOST_ARM) + { + if ((p_end - p_start) <= 16) + error("%s: function too small", name); + if (get32((uint32_t *)p_start) != 0xe1a0c00d || + (get32((uint32_t *)(p_start + 4)) & 0xffff0000) != 0xe92d0000 || + get32((uint32_t *)(p_start + 8)) != 0xe24cb004) + error("%s: invalid prolog", name); + p_start += 12; + start_offset += 12; + copy_size = arm_emit_ldr_info(name, start_offset, NULL, p_start, p_end, + relocs, nb_relocs); + } +#elif defined(HOST_M68K) + { + uint8_t *p; + p = (void *)(p_end - 2); + if (p == p_start) + error("empty code for %s", name); + // remove NOP's, probably added for alignment + while ((get16((uint16_t *)p) == 0x4e71) && + (p>p_start)) + p -= 2; + if (get16((uint16_t *)p) != 0x4e75) + error("rts expected at the end of %s", name); + copy_size = p - p_start; + } +#else +#error unsupported CPU +#endif + + /* compute the number of arguments by looking at the relocations */ + for(i = 0;i < MAX_ARGS; i++) + args_present[i] = 0; + + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + host_ulong offset = get_rel_offset(rel); + if (offset >= start_offset && + offset < start_offset + (p_end - p_start)) { + sym_name = get_rel_sym_name(rel); + if(!sym_name) + continue; + if (strstart(sym_name, "__op_param", &p) || + strstart(sym_name, "__op_gen_label", &p)) { + n = strtoul(p, NULL, 10); + if (n > MAX_ARGS) + error("too many arguments in %s", name); + args_present[n - 1] = 1; + } + } + } + + nb_args = 0; + while (nb_args < MAX_ARGS && args_present[nb_args]) + nb_args++; + for(i = nb_args; i < MAX_ARGS; i++) { + if (args_present[i]) + error("inconsistent argument numbering in %s", name); + } + + if (gen_switch == 2) { + fprintf(outfile, "DEF(%s, %d, %d)\n", name + 3, nb_args, copy_size); + } else if (gen_switch == 1) { + + /* output C code */ + fprintf(outfile, "case INDEX_%s: {\n", name); + if (nb_args > 0) { + fprintf(outfile, " long "); + for(i = 0; i < nb_args; i++) { + if (i != 0) + fprintf(outfile, ", "); + fprintf(outfile, "param%d", i + 1); + } + fprintf(outfile, ";\n"); + } +#if defined(HOST_IA64) + fprintf(outfile, " extern char %s;\n", name); +#else + fprintf(outfile, " extern void %s();\n", name); +#endif + + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + host_ulong offset = get_rel_offset(rel); + if (offset >= start_offset && + offset < start_offset + (p_end - p_start)) { + sym_name = get_rel_sym_name(rel); + if(!sym_name) + continue; + if (*sym_name && + !strstart(sym_name, "__op_param", NULL) && + !strstart(sym_name, "__op_jmp", NULL) && + !strstart(sym_name, "__op_gen_label", NULL)) { +#if defined(HOST_SPARC) + if (sym_name[0] == '.') { + fprintf(outfile, + "extern char __dot_%s __asm__(\"%s\");\n", + sym_name+1, sym_name); + continue; + } +#endif +#if defined(__APPLE__) +/* set __attribute((unused)) on darwin because we wan't to avoid warning when we don't use the symbol */ + fprintf(outfile, "extern char %s __attribute__((unused));\n", sym_name); +#elif defined(HOST_IA64) + if (ELF64_R_TYPE(rel->r_info) != R_IA64_PCREL21B) + /* + * PCREL21 br.call targets generally + * are out of range and need to go + * through an "import stub". + */ + fprintf(outfile, " extern char %s;\n", + sym_name); +#else + fprintf(outfile, "extern char %s;\n", sym_name); +#endif + } + } + } + + fprintf(outfile, " memcpy(gen_code_ptr, (void *)((char *)&%s+%d), %d);\n", + name, (int)(start_offset - offset), copy_size); + + /* emit code offset information */ + { + EXE_SYM *sym; + const char *sym_name, *p; + unsigned long val; + int n; + + for(i = 0, sym = symtab; i < nb_syms; i++, sym++) { + sym_name = get_sym_name(sym); + if (strstart(sym_name, "__op_label", &p)) { + uint8_t *ptr; + unsigned long offset; + + /* test if the variable refers to a label inside + the code we are generating */ +#ifdef CONFIG_FORMAT_COFF + if (sym->st_shndx == text_shndx) { + ptr = sdata[coff_text_shndx]; + } else if (sym->st_shndx == data_shndx) { + ptr = sdata[coff_data_shndx]; + } else { + ptr = NULL; + } +#elif defined(CONFIG_FORMAT_MACH) + if(!sym->n_sect) + continue; + ptr = sdata[sym->n_sect-1]; +#else + ptr = sdata[sym->st_shndx]; +#endif + if (!ptr) + error("__op_labelN in invalid section"); + offset = sym->st_value; +#ifdef CONFIG_FORMAT_MACH + offset -= section_hdr[sym->n_sect-1].addr; +#endif + val = *(unsigned long *)(ptr + offset); +#ifdef ELF_USES_RELOCA + { + int reloc_shndx, nb_relocs1, j; + + /* try to find a matching relocation */ + reloc_shndx = find_reloc(sym->st_shndx); + if (reloc_shndx) { + nb_relocs1 = shdr[reloc_shndx].sh_size / + shdr[reloc_shndx].sh_entsize; + rel = (ELF_RELOC *)sdata[reloc_shndx]; + for(j = 0; j < nb_relocs1; j++) { + if (rel->r_offset == offset) { + val = rel->r_addend; + break; + } + rel++; + } + } + } +#endif + if (val >= start_offset && val <= start_offset + copy_size) { + n = strtol(p, NULL, 10); + fprintf(outfile, " label_offsets[%d] = %ld + (gen_code_ptr - gen_code_buf);\n", n, (long)(val - start_offset)); + } + } + } + } + + /* load parameres in variables */ + for(i = 0; i < nb_args; i++) { + fprintf(outfile, " param%d = *opparam_ptr++;\n", i + 1); + } + + /* patch relocations */ +#if defined(HOST_I386) + { + char name[256]; + int type; + int addend; + int reloc_offset; + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + if (rel->r_offset >= start_offset && + rel->r_offset < start_offset + copy_size) { + sym_name = get_rel_sym_name(rel); + if (!sym_name) + continue; + reloc_offset = rel->r_offset - start_offset; + if (strstart(sym_name, "__op_jmp", &p)) { + int n; + n = strtol(p, NULL, 10); + /* __op_jmp relocations are done at + runtime to do translated block + chaining: the offset of the instruction + needs to be stored */ + fprintf(outfile, " jmp_offsets[%d] = %d + (gen_code_ptr - gen_code_buf);\n", + n, reloc_offset); + continue; + } + + get_reloc_expr(name, sizeof(name), sym_name); + addend = get32((uint32_t *)(text + rel->r_offset)); +#ifdef CONFIG_FORMAT_ELF + type = ELF32_R_TYPE(rel->r_info); + switch(type) { + case R_386_32: + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", + reloc_offset, name, addend); + break; + case R_386_PC32: + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %d) + %d;\n", + reloc_offset, name, reloc_offset, addend); + break; + default: + error("unsupported i386 relocation (%d)", type); + } +#elif defined(CONFIG_FORMAT_COFF) + { + char *temp_name; + int j; + EXE_SYM *sym; + temp_name = get_sym_name(symtab + *(uint32_t *)(rel->r_reloc->r_symndx)); + if (!strcmp(temp_name, ".data")) { + for (j = 0, sym = symtab; j < nb_syms; j++, sym++) { + if (strstart(sym->st_name, sym_name, NULL)) { + addend -= sym->st_value; + } + } + } + } + type = rel->r_type; + switch(type) { + case DIR32: + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", + reloc_offset, name, addend); + break; + case DISP32: + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %d) + %d -4;\n", + reloc_offset, name, reloc_offset, addend); + break; + default: + error("unsupported i386 relocation (%d)", type); + } +#else +#error unsupport object format +#endif + } + } + } +#elif defined(HOST_X86_64) + { + char name[256]; + int type; + int addend; + int reloc_offset; + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + if (rel->r_offset >= start_offset && + rel->r_offset < start_offset + copy_size) { + sym_name = strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name; + get_reloc_expr(name, sizeof(name), sym_name); + type = ELF32_R_TYPE(rel->r_info); + addend = rel->r_addend; + reloc_offset = rel->r_offset - start_offset; + switch(type) { + case R_X86_64_32: + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = (uint32_t)%s + %d;\n", + reloc_offset, name, addend); + break; + case R_X86_64_32S: + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = (int32_t)%s + %d;\n", + reloc_offset, name, addend); + break; + case R_X86_64_PC32: + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %d) + %d;\n", + reloc_offset, name, reloc_offset, addend); + break; + default: + error("unsupported X86_64 relocation (%d)", type); + } + } + } + } +#elif defined(HOST_PPC) + { +#ifdef CONFIG_FORMAT_ELF + char name[256]; + int type; + int addend; + int reloc_offset; + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + if (rel->r_offset >= start_offset && + rel->r_offset < start_offset + copy_size) { + sym_name = strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name; + reloc_offset = rel->r_offset - start_offset; + if (strstart(sym_name, "__op_jmp", &p)) { + int n; + n = strtol(p, NULL, 10); + /* __op_jmp relocations are done at + runtime to do translated block + chaining: the offset of the instruction + needs to be stored */ + fprintf(outfile, " jmp_offsets[%d] = %d + (gen_code_ptr - gen_code_buf);\n", + n, reloc_offset); + continue; + } + + get_reloc_expr(name, sizeof(name), sym_name); + type = ELF32_R_TYPE(rel->r_info); + addend = rel->r_addend; + switch(type) { + case R_PPC_ADDR32: + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", + reloc_offset, name, addend); + break; + case R_PPC_ADDR16_LO: + fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d) = (%s + %d);\n", + reloc_offset, name, addend); + break; + case R_PPC_ADDR16_HI: + fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d) = (%s + %d) >> 16;\n", + reloc_offset, name, addend); + break; + case R_PPC_ADDR16_HA: + fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d) = (%s + %d + 0x8000) >> 16;\n", + reloc_offset, name, addend); + break; + case R_PPC_REL24: + /* warning: must be at 32 MB distancy */ + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = (*(uint32_t *)(gen_code_ptr + %d) & ~0x03fffffc) | ((%s - (long)(gen_code_ptr + %d) + %d) & 0x03fffffc);\n", + reloc_offset, reloc_offset, name, reloc_offset, addend); + break; + default: + error("unsupported powerpc relocation (%d)", type); + } + } + } +#elif defined(CONFIG_FORMAT_MACH) + struct scattered_relocation_info *scarel; + struct relocation_info * rel; + char final_sym_name[256]; + const char *sym_name; + const char *p; + int slide, sslide; + int i; + + for(i = 0, rel = relocs; i < nb_relocs; i++, rel++) { + unsigned int offset, length, value = 0; + unsigned int type, pcrel, isym = 0; + unsigned int usesym = 0; + + if(R_SCATTERED & rel->r_address) { + scarel = (struct scattered_relocation_info*)rel; + offset = (unsigned int)scarel->r_address; + length = scarel->r_length; + pcrel = scarel->r_pcrel; + type = scarel->r_type; + value = scarel->r_value; + } else { + value = isym = rel->r_symbolnum; + usesym = (rel->r_extern); + offset = rel->r_address; + length = rel->r_length; + pcrel = rel->r_pcrel; + type = rel->r_type; + } + + slide = offset - start_offset; + + if (!(offset >= start_offset && offset < start_offset + size)) + continue; /* not in our range */ + + sym_name = get_reloc_name(rel, &sslide); + + if(usesym && symtab[isym].n_type & N_STAB) + continue; /* don't handle STAB (debug sym) */ + + if (sym_name && strstart(sym_name, "__op_jmp", &p)) { + int n; + n = strtol(p, NULL, 10); + fprintf(outfile, " jmp_offsets[%d] = %d + (gen_code_ptr - gen_code_buf);\n", + n, slide); + continue; /* Nothing more to do */ + } + + if(!sym_name) + { + fprintf(outfile, "/* #warning relocation not handled in %s (value 0x%x, %s, offset 0x%x, length 0x%x, %s, type 0x%x) */\n", + name, value, usesym ? "use sym" : "don't use sym", offset, length, pcrel ? "pcrel":"", type); + continue; /* dunno how to handle without final_sym_name */ + } + + get_reloc_expr(final_sym_name, sizeof(final_sym_name), + sym_name); + switch(type) { + case PPC_RELOC_BR24: + if (!strstart(sym_name,"__op_gen_label",&p)) { + fprintf(outfile, "{\n"); + fprintf(outfile, " uint32_t imm = *(uint32_t *)(gen_code_ptr + %d) & 0x3fffffc;\n", slide); + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = (*(uint32_t *)(gen_code_ptr + %d) & ~0x03fffffc) | ((imm + ((long)%s - (long)gen_code_ptr) + %d) & 0x03fffffc);\n", + slide, slide, name, sslide ); + fprintf(outfile, "}\n"); + } else { + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = (*(uint32_t *)(gen_code_ptr + %d) & ~0x03fffffc) | (((long)%s - (long)gen_code_ptr - %d) & 0x03fffffc);\n", + slide, slide, final_sym_name, slide); + } + break; + case PPC_RELOC_HI16: + fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d + 2) = (%s + %d) >> 16;\n", + slide, final_sym_name, sslide); + break; + case PPC_RELOC_LO16: + fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d + 2) = (%s + %d);\n", + slide, final_sym_name, sslide); + break; + case PPC_RELOC_HA16: + fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d + 2) = (%s + %d + 0x8000) >> 16;\n", + slide, final_sym_name, sslide); + break; + default: + error("unsupported powerpc relocation (%d)", type); + } + } +#else +#error unsupport object format +#endif + } +#elif defined(HOST_S390) + { + char name[256]; + int type; + int addend; + int reloc_offset; + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + if (rel->r_offset >= start_offset && + rel->r_offset < start_offset + copy_size) { + sym_name = strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name; + get_reloc_expr(name, sizeof(name), sym_name); + type = ELF32_R_TYPE(rel->r_info); + addend = rel->r_addend; + reloc_offset = rel->r_offset - start_offset; + switch(type) { + case R_390_32: + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", + reloc_offset, name, addend); + break; + case R_390_16: + fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %d) = %s + %d;\n", + reloc_offset, name, addend); + break; + case R_390_8: + fprintf(outfile, " *(uint8_t *)(gen_code_ptr + %d) = %s + %d;\n", + reloc_offset, name, addend); + break; + default: + error("unsupported s390 relocation (%d)", type); + } + } + } + } +#elif defined(HOST_ALPHA) + { + for (i = 0, rel = relocs; i < nb_relocs; i++, rel++) { + if (rel->r_offset >= start_offset && rel->r_offset < start_offset + copy_size) { + int type; + long reloc_offset; + + type = ELF64_R_TYPE(rel->r_info); + sym_name = strtab + symtab[ELF64_R_SYM(rel->r_info)].st_name; + reloc_offset = rel->r_offset - start_offset; + switch (type) { + case R_ALPHA_GPDISP: + /* The gp is just 32 bit, and never changes, so it's easiest to emit it + as an immediate instead of constructing it from the pv or ra. */ + fprintf(outfile, " immediate_ldah(gen_code_ptr + %ld, gp);\n", + reloc_offset); + fprintf(outfile, " immediate_lda(gen_code_ptr + %ld, gp);\n", + reloc_offset + (int)rel->r_addend); + break; + case R_ALPHA_LITUSE: + /* jsr to literal hint. Could be used to optimize to bsr. Ignore for + now, since some called functions (libc) need pv to be set up. */ + break; + case R_ALPHA_HINT: + /* Branch target prediction hint. Ignore for now. Should be already + correct for in-function jumps. */ + break; + case R_ALPHA_LITERAL: + /* Load a literal from the GOT relative to the gp. Since there's only a + single gp, nothing is to be done. */ + break; + case R_ALPHA_GPRELHIGH: + /* Handle fake relocations against __op_param symbol. Need to emit the + high part of the immediate value instead. Other symbols need no + special treatment. */ + if (strstart(sym_name, "__op_param", &p)) + fprintf(outfile, " immediate_ldah(gen_code_ptr + %ld, param%s);\n", + reloc_offset, p); + break; + case R_ALPHA_GPRELLOW: + if (strstart(sym_name, "__op_param", &p)) + fprintf(outfile, " immediate_lda(gen_code_ptr + %ld, param%s);\n", + reloc_offset, p); + break; + case R_ALPHA_BRSGP: + /* PC-relative jump. Tweak offset to skip the two instructions that try to + set up the gp from the pv. */ + fprintf(outfile, " fix_bsr(gen_code_ptr + %ld, (uint8_t *) &%s - (gen_code_ptr + %ld + 4) + 8);\n", + reloc_offset, sym_name, reloc_offset); + break; + default: + error("unsupported Alpha relocation (%d)", type); + } + } + } + } +#elif defined(HOST_IA64) + { + unsigned long sym_idx; + long code_offset; + char name[256]; + int type; + long addend; + + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + sym_idx = ELF64_R_SYM(rel->r_info); + if (rel->r_offset < start_offset + || rel->r_offset >= start_offset + copy_size) + continue; + sym_name = (strtab + symtab[sym_idx].st_name); + code_offset = rel->r_offset - start_offset; + if (strstart(sym_name, "__op_jmp", &p)) { + int n; + n = strtol(p, NULL, 10); + /* __op_jmp relocations are done at + runtime to do translated block + chaining: the offset of the instruction + needs to be stored */ + fprintf(outfile, " jmp_offsets[%d] =" + "%ld + (gen_code_ptr - gen_code_buf);\n", + n, code_offset); + continue; + } + get_reloc_expr(name, sizeof(name), sym_name); + type = ELF64_R_TYPE(rel->r_info); + addend = rel->r_addend; + switch(type) { + case R_IA64_IMM64: + fprintf(outfile, + " ia64_imm64(gen_code_ptr + %ld, " + "%s + %ld);\n", + code_offset, name, addend); + break; + case R_IA64_LTOFF22X: + case R_IA64_LTOFF22: + fprintf(outfile, " IA64_LTOFF(gen_code_ptr + %ld," + " %s + %ld, %d);\n", + code_offset, name, addend, + (type == R_IA64_LTOFF22X)); + break; + case R_IA64_LDXMOV: + fprintf(outfile, + " ia64_ldxmov(gen_code_ptr + %ld," + " %s + %ld);\n", code_offset, name, addend); + break; + + case R_IA64_PCREL21B: + if (strstart(sym_name, "__op_gen_label", NULL)) { + fprintf(outfile, + " ia64_imm21b(gen_code_ptr + %ld," + " (long) (%s + %ld -\n\t\t" + "((long) gen_code_ptr + %ld)) >> 4);\n", + code_offset, name, addend, + code_offset & ~0xfUL); + } else { + fprintf(outfile, + " IA64_PLT(gen_code_ptr + %ld, " + "%d);\t/* %s + %ld */\n", + code_offset, + get_plt_index(sym_name, addend), + sym_name, addend); + } + break; + default: + error("unsupported ia64 relocation (0x%x)", + type); + } + } + fprintf(outfile, " ia64_nop_b(gen_code_ptr + %d);\n", + copy_size - 16 + 2); + } +#elif defined(HOST_SPARC) + { + char name[256]; + int type; + int addend; + int reloc_offset; + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + if (rel->r_offset >= start_offset && + rel->r_offset < start_offset + copy_size) { + sym_name = strtab + symtab[ELF32_R_SYM(rel->r_info)].st_name; + get_reloc_expr(name, sizeof(name), sym_name); + type = ELF32_R_TYPE(rel->r_info); + addend = rel->r_addend; + reloc_offset = rel->r_offset - start_offset; + switch(type) { + case R_SPARC_32: + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", + reloc_offset, name, addend); + break; + case R_SPARC_HI22: + fprintf(outfile, + " *(uint32_t *)(gen_code_ptr + %d) = " + "((*(uint32_t *)(gen_code_ptr + %d)) " + " & ~0x3fffff) " + " | (((%s + %d) >> 10) & 0x3fffff);\n", + reloc_offset, reloc_offset, name, addend); + break; + case R_SPARC_LO10: + fprintf(outfile, + " *(uint32_t *)(gen_code_ptr + %d) = " + "((*(uint32_t *)(gen_code_ptr + %d)) " + " & ~0x3ff) " + " | ((%s + %d) & 0x3ff);\n", + reloc_offset, reloc_offset, name, addend); + break; + case R_SPARC_WDISP30: + fprintf(outfile, + " *(uint32_t *)(gen_code_ptr + %d) = " + "((*(uint32_t *)(gen_code_ptr + %d)) " + " & ~0x3fffffff) " + " | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) " + " & 0x3fffffff);\n", + reloc_offset, reloc_offset, name, addend, + reloc_offset); + break; + default: + error("unsupported sparc relocation (%d)", type); + } + } + } + } +#elif defined(HOST_SPARC64) + { + char name[256]; + int type; + int addend; + int reloc_offset; + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + if (rel->r_offset >= start_offset && + rel->r_offset < start_offset + copy_size) { + sym_name = strtab + symtab[ELF64_R_SYM(rel->r_info)].st_name; + get_reloc_expr(name, sizeof(name), sym_name); + type = ELF64_R_TYPE(rel->r_info); + addend = rel->r_addend; + reloc_offset = rel->r_offset - start_offset; + switch(type) { + case R_SPARC_32: + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", + reloc_offset, name, addend); + break; + case R_SPARC_HI22: + fprintf(outfile, + " *(uint32_t *)(gen_code_ptr + %d) = " + "((*(uint32_t *)(gen_code_ptr + %d)) " + " & ~0x3fffff) " + " | (((%s + %d) >> 10) & 0x3fffff);\n", + reloc_offset, reloc_offset, name, addend); + break; + case R_SPARC_LO10: + fprintf(outfile, + " *(uint32_t *)(gen_code_ptr + %d) = " + "((*(uint32_t *)(gen_code_ptr + %d)) " + " & ~0x3ff) " + " | ((%s + %d) & 0x3ff);\n", + reloc_offset, reloc_offset, name, addend); + break; + case R_SPARC_WDISP30: + fprintf(outfile, + " *(uint32_t *)(gen_code_ptr + %d) = " + "((*(uint32_t *)(gen_code_ptr + %d)) " + " & ~0x3fffffff) " + " | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) " + " & 0x3fffffff);\n", + reloc_offset, reloc_offset, name, addend, + reloc_offset); + break; + default: + error("unsupported sparc64 relocation (%d)", type); + } + } + } + } +#elif defined(HOST_ARM) + { + char name[256]; + int type; + int addend; + int reloc_offset; + + arm_emit_ldr_info(name, start_offset, outfile, p_start, p_end, + relocs, nb_relocs); + + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + if (rel->r_offset >= start_offset && + rel->r_offset < start_offset + copy_size) { + sym_name = strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name; + /* the compiler leave some unnecessary references to the code */ + if (sym_name[0] == '\0') + continue; + get_reloc_expr(name, sizeof(name), sym_name); + type = ELF32_R_TYPE(rel->r_info); + addend = get32((uint32_t *)(text + rel->r_offset)); + reloc_offset = rel->r_offset - start_offset; + switch(type) { + case R_ARM_ABS32: + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", + reloc_offset, name, addend); + break; + case R_ARM_PC24: + fprintf(outfile, " arm_reloc_pc24((uint32_t *)(gen_code_ptr + %d), 0x%x, %s);\n", + reloc_offset, addend, name); + break; + default: + error("unsupported arm relocation (%d)", type); + } + } + } + } +#elif defined(HOST_M68K) + { + char name[256]; + int type; + int addend; + int reloc_offset; + Elf32_Sym *sym; + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + if (rel->r_offset >= start_offset && + rel->r_offset < start_offset + copy_size) { + sym = &(symtab[ELFW(R_SYM)(rel->r_info)]); + sym_name = strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name; + get_reloc_expr(name, sizeof(name), sym_name); + type = ELF32_R_TYPE(rel->r_info); + addend = get32((uint32_t *)(text + rel->r_offset)) + rel->r_addend; + reloc_offset = rel->r_offset - start_offset; + switch(type) { + case R_68K_32: + fprintf(outfile, " /* R_68K_32 RELOC, offset %x */\n", rel->r_offset) ; + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s + %#x;\n", + reloc_offset, name, addend ); + break; + case R_68K_PC32: + fprintf(outfile, " /* R_68K_PC32 RELOC, offset %x */\n", rel->r_offset); + fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %#x) + %#x;\n", + reloc_offset, name, reloc_offset, /*sym->st_value+*/ addend); + break; + default: + error("unsupported m68k relocation (%d)", type); + } + } + } + } +#else +#error unsupported CPU +#endif + fprintf(outfile, " gen_code_ptr += %d;\n", copy_size); + fprintf(outfile, "}\n"); + fprintf(outfile, "break;\n\n"); + } else { + fprintf(outfile, "static inline void gen_%s(", name); + if (nb_args == 0) { + fprintf(outfile, "void"); + } else { + for(i = 0; i < nb_args; i++) { + if (i != 0) + fprintf(outfile, ", "); + fprintf(outfile, "long param%d", i + 1); + } + } + fprintf(outfile, ")\n"); + fprintf(outfile, "{\n"); + for(i = 0; i < nb_args; i++) { + fprintf(outfile, " *gen_opparam_ptr++ = param%d;\n", i + 1); + } + fprintf(outfile, " *gen_opc_ptr++ = INDEX_%s;\n", name); + fprintf(outfile, "}\n\n"); + } +} + +int gen_file(FILE *outfile, int out_type) +{ + int i; + EXE_SYM *sym; + + if (out_type == OUT_INDEX_OP) { + fprintf(outfile, "DEF(end, 0, 0)\n"); + fprintf(outfile, "DEF(nop, 0, 0)\n"); + fprintf(outfile, "DEF(nop1, 1, 0)\n"); + fprintf(outfile, "DEF(nop2, 2, 0)\n"); + fprintf(outfile, "DEF(nop3, 3, 0)\n"); + for(i = 0, sym = symtab; i < nb_syms; i++, sym++) { + const char *name; + name = get_sym_name(sym); + if (strstart(name, OP_PREFIX, NULL)) { + gen_code(name, sym->st_value, sym->st_size, outfile, 2); + } + } + } else if (out_type == OUT_GEN_OP) { + /* generate gen_xxx functions */ + fprintf(outfile, "#include \"dyngen-op.h\"\n"); + for(i = 0, sym = symtab; i < nb_syms; i++, sym++) { + const char *name; + name = get_sym_name(sym); + if (strstart(name, OP_PREFIX, NULL)) { +#if defined(CONFIG_FORMAT_ELF) || defined(CONFIG_FORMAT_COFF) + if (sym->st_shndx != text_shndx) + error("invalid section for opcode (0x%x)", sym->st_shndx); +#endif + gen_code(name, sym->st_value, sym->st_size, outfile, 0); + } + } + + } else { + /* generate big code generation switch */ +fprintf(outfile, +"int dyngen_code(uint8_t *gen_code_buf,\n" +" uint16_t *label_offsets, uint16_t *jmp_offsets,\n" +" const uint16_t *opc_buf, const uint32_t *opparam_buf, const long *gen_labels)\n" +"{\n" +" uint8_t *gen_code_ptr;\n" +" const uint16_t *opc_ptr;\n" +" const uint32_t *opparam_ptr;\n"); + +#ifdef HOST_ARM +fprintf(outfile, +" uint8_t *last_gen_code_ptr = gen_code_buf;\n" +" LDREntry *arm_ldr_ptr = arm_ldr_table;\n" +" uint32_t *arm_data_ptr = arm_data_table;\n"); +#endif +#ifdef HOST_IA64 + { + long addend, not_first = 0; + unsigned long sym_idx; + int index, max_index; + const char *sym_name; + EXE_RELOC *rel; + + max_index = -1; + for (i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + sym_idx = ELF64_R_SYM(rel->r_info); + sym_name = (strtab + symtab[sym_idx].st_name); + if (strstart(sym_name, "__op_gen_label", NULL)) + continue; + if (ELF64_R_TYPE(rel->r_info) != R_IA64_PCREL21B) + continue; + + addend = rel->r_addend; + index = get_plt_index(sym_name, addend); + if (index <= max_index) + continue; + max_index = index; + fprintf(outfile, " extern void %s(void);\n", sym_name); + } + + fprintf(outfile, + " struct ia64_fixup *plt_fixes = NULL, " + "*ltoff_fixes = NULL;\n" + " static long plt_target[] = {\n\t"); + + max_index = -1; + for (i = 0, rel = relocs;i < nb_relocs; i++, rel++) { + sym_idx = ELF64_R_SYM(rel->r_info); + sym_name = (strtab + symtab[sym_idx].st_name); + if (strstart(sym_name, "__op_gen_label", NULL)) + continue; + if (ELF64_R_TYPE(rel->r_info) != R_IA64_PCREL21B) + continue; + + addend = rel->r_addend; + index = get_plt_index(sym_name, addend); + if (index <= max_index) + continue; + max_index = index; + + if (not_first) + fprintf(outfile, ",\n\t"); + not_first = 1; + if (addend) + fprintf(outfile, "(long) &%s + %ld", sym_name, addend); + else + fprintf(outfile, "(long) &%s", sym_name); + } + fprintf(outfile, "\n };\n" + " unsigned int plt_offset[%u] = { 0 };\n", max_index + 1); + } +#endif + +fprintf(outfile, +"\n" +" gen_code_ptr = gen_code_buf;\n" +" opc_ptr = opc_buf;\n" +" opparam_ptr = opparam_buf;\n"); + + /* Generate prologue, if needed. */ + +fprintf(outfile, +" for(;;) {\n" +" switch(*opc_ptr++) {\n" +); + + for(i = 0, sym = symtab; i < nb_syms; i++, sym++) { + const char *name; + name = get_sym_name(sym); + if (strstart(name, OP_PREFIX, NULL)) { +#if 0 + printf("%4d: %s pos=0x%08x len=%d\n", + i, name, sym->st_value, sym->st_size); +#endif +#if defined(CONFIG_FORMAT_ELF) || defined(CONFIG_FORMAT_COFF) + if (sym->st_shndx != text_shndx) + error("invalid section for opcode (0x%x)", sym->st_shndx); +#endif + gen_code(name, sym->st_value, sym->st_size, outfile, 1); + } + } + +fprintf(outfile, +" case INDEX_op_nop:\n" +" break;\n" +" case INDEX_op_nop1:\n" +" opparam_ptr++;\n" +" break;\n" +" case INDEX_op_nop2:\n" +" opparam_ptr += 2;\n" +" break;\n" +" case INDEX_op_nop3:\n" +" opparam_ptr += 3;\n" +" break;\n" +" default:\n" +" goto the_end;\n" +" }\n"); + +#ifdef HOST_ARM +/* generate constant table if needed */ +fprintf(outfile, +" if ((gen_code_ptr - last_gen_code_ptr) >= (MAX_FRAG_SIZE - MAX_OP_SIZE)) {\n" +" gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, arm_ldr_ptr, arm_data_table, arm_data_ptr, 1);\n" +" last_gen_code_ptr = gen_code_ptr;\n" +" arm_ldr_ptr = arm_ldr_table;\n" +" arm_data_ptr = arm_data_table;\n" +" }\n"); +#endif + + +fprintf(outfile, +" }\n" +" the_end:\n" +); +#ifdef HOST_IA64 + fprintf(outfile, + " {\n" + " extern char code_gen_buffer[];\n" + " ia64_apply_fixes(&gen_code_ptr, ltoff_fixes, " + "(uint64_t) code_gen_buffer + 2*(1<<20), plt_fixes,\n\t\t\t" + "sizeof(plt_target)/sizeof(plt_target[0]),\n\t\t\t" + "plt_target, plt_offset);\n }\n"); +#endif + +/* generate some code patching */ +#ifdef HOST_ARM +fprintf(outfile, "gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, arm_ldr_ptr, arm_data_table, arm_data_ptr, 0);\n"); +#endif + /* flush instruction cache */ + fprintf(outfile, "flush_icache_range((unsigned long)gen_code_buf, (unsigned long)gen_code_ptr);\n"); + + fprintf(outfile, "return gen_code_ptr - gen_code_buf;\n"); + fprintf(outfile, "}\n\n"); + + } + + return 0; +} + +void usage(void) +{ + printf("dyngen (c) 2003 Fabrice Bellard\n" + "usage: dyngen [-o outfile] [-c] objfile\n" + "Generate a dynamic code generator from an object file\n" + "-c output enum of operations\n" + "-g output gen_op_xx() functions\n" + ); + exit(1); +} + +int main(int argc, char **argv) +{ + int c, out_type; + const char *filename, *outfilename; + FILE *outfile; + + outfilename = "out.c"; + out_type = OUT_CODE; + for(;;) { + c = getopt(argc, argv, "ho:cg"); + if (c == -1) + break; + switch(c) { + case 'h': + usage(); + break; + case 'o': + outfilename = optarg; + break; + case 'c': + out_type = OUT_INDEX_OP; + break; + case 'g': + out_type = OUT_GEN_OP; + break; + } + } + if (optind >= argc) + usage(); + filename = argv[optind]; + outfile = fopen(outfilename, "w"); + if (!outfile) + error("could not open '%s'", outfilename); + + load_object(filename); + gen_file(outfile, out_type); + fclose(outfile); + return 0; +} diff --git a/tools/ioemu/dyngen.h b/tools/ioemu/dyngen.h new file mode 100644 index 0000000000..5bb170e94d --- /dev/null +++ b/tools/ioemu/dyngen.h @@ -0,0 +1,429 @@ +/* + * dyngen helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int __op_param1, __op_param2, __op_param3; +int __op_gen_label1, __op_gen_label2, __op_gen_label3; +int __op_jmp0, __op_jmp1, __op_jmp2, __op_jmp3; + +#ifdef __i386__ +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ +} +#endif + +#ifdef __x86_64__ +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ +} +#endif + +#ifdef __s390__ +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ +} +#endif + +#ifdef __ia64__ +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ + while (start < stop) { + asm volatile ("fc %0" :: "r"(start)); + start += 32; + } + asm volatile (";;sync.i;;srlz.i;;"); +} +#endif + +#ifdef __powerpc__ + +#define MIN_CACHE_LINE_SIZE 8 /* conservative value */ + +static void inline flush_icache_range(unsigned long start, unsigned long stop) +{ + unsigned long p; + + start &= ~(MIN_CACHE_LINE_SIZE - 1); + stop = (stop + MIN_CACHE_LINE_SIZE - 1) & ~(MIN_CACHE_LINE_SIZE - 1); + + for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) { + asm volatile ("dcbst 0,%0" : : "r"(p) : "memory"); + } + asm volatile ("sync" : : : "memory"); + for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) { + asm volatile ("icbi 0,%0" : : "r"(p) : "memory"); + } + asm volatile ("sync" : : : "memory"); + asm volatile ("isync" : : : "memory"); +} +#endif + +#ifdef __alpha__ +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ + asm ("imb"); +} +#endif + +#ifdef __sparc__ + +static void inline flush_icache_range(unsigned long start, unsigned long stop) +{ + unsigned long p; + + p = start & ~(8UL - 1UL); + stop = (stop + (8UL - 1UL)) & ~(8UL - 1UL); + + for (; p < stop; p += 8) + __asm__ __volatile__("flush\t%0" : : "r" (p)); +} + +#endif + +#ifdef __arm__ +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ + register unsigned long _beg __asm ("a1") = start; + register unsigned long _end __asm ("a2") = stop; + register unsigned long _flg __asm ("a3") = 0; + __asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg)); +} +#endif + +#ifdef __mc68000 +#include +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ + cacheflush(start,FLUSH_SCOPE_LINE,FLUSH_CACHE_BOTH,stop-start+16); +} +#endif + +#ifdef __alpha__ + +register int gp asm("$29"); + +static inline void immediate_ldah(void *p, int val) { + uint32_t *dest = p; + long high = ((val >> 16) + ((val >> 15) & 1)) & 0xffff; + + *dest &= ~0xffff; + *dest |= high; + *dest |= 31 << 16; +} +static inline void immediate_lda(void *dest, int val) { + *(uint16_t *) dest = val; +} +void fix_bsr(void *p, int offset) { + uint32_t *dest = p; + *dest &= ~((1 << 21) - 1); + *dest |= (offset >> 2) & ((1 << 21) - 1); +} + +#endif /* __alpha__ */ + +#ifdef __arm__ + +#define MAX_OP_SIZE (128 * 4) /* in bytes */ +/* max size of the code that can be generated without calling arm_flush_ldr */ +#define MAX_FRAG_SIZE (1024 * 4) +//#define MAX_FRAG_SIZE (135 * 4) /* for testing */ + +typedef struct LDREntry { + uint8_t *ptr; + uint32_t *data_ptr; +} LDREntry; + +static LDREntry arm_ldr_table[1024]; +static uint32_t arm_data_table[1024]; + +extern char exec_loop; + +static inline void arm_reloc_pc24(uint32_t *ptr, uint32_t insn, int val) +{ + *ptr = (insn & ~0xffffff) | ((insn + ((val - (int)ptr) >> 2)) & 0xffffff); +} + +static uint8_t *arm_flush_ldr(uint8_t *gen_code_ptr, + LDREntry *ldr_start, LDREntry *ldr_end, + uint32_t *data_start, uint32_t *data_end, + int gen_jmp) +{ + LDREntry *le; + uint32_t *ptr; + int offset, data_size, target; + uint8_t *data_ptr; + uint32_t insn; + + data_size = (uint8_t *)data_end - (uint8_t *)data_start; + + if (gen_jmp) { + /* generate branch to skip the data */ + if (data_size == 0) + return gen_code_ptr; + target = (long)gen_code_ptr + data_size + 4; + arm_reloc_pc24((uint32_t *)gen_code_ptr, 0xeafffffe, target); + gen_code_ptr += 4; + } + + /* copy the data */ + data_ptr = gen_code_ptr; + memcpy(gen_code_ptr, data_start, data_size); + gen_code_ptr += data_size; + + /* patch the ldr to point to the data */ + for(le = ldr_start; le < ldr_end; le++) { + ptr = (uint32_t *)le->ptr; + offset = ((unsigned long)(le->data_ptr) - (unsigned long)data_start) + + (unsigned long)data_ptr - + (unsigned long)ptr - 8; + insn = *ptr & ~(0xfff | 0x00800000); + if (offset < 0) { + offset = - offset; + } else { + insn |= 0x00800000; + } + if (offset > 0xfff) { + fprintf(stderr, "Error ldr offset\n"); + abort(); + } + insn |= offset; + *ptr = insn; + } + return gen_code_ptr; +} + +#endif /* __arm__ */ + +#ifdef __ia64 + + +/* Patch instruction with "val" where "mask" has 1 bits. */ +static inline void ia64_patch (uint64_t insn_addr, uint64_t mask, uint64_t val) +{ + uint64_t m0, m1, v0, v1, b0, b1, *b = (uint64_t *) (insn_addr & -16); +# define insn_mask ((1UL << 41) - 1) + unsigned long shift; + + b0 = b[0]; b1 = b[1]; + shift = 5 + 41 * (insn_addr % 16); /* 5 template, 3 x 41-bit insns */ + if (shift >= 64) { + m1 = mask << (shift - 64); + v1 = val << (shift - 64); + } else { + m0 = mask << shift; m1 = mask >> (64 - shift); + v0 = val << shift; v1 = val >> (64 - shift); + b[0] = (b0 & ~m0) | (v0 & m0); + } + b[1] = (b1 & ~m1) | (v1 & m1); +} + +static inline void ia64_patch_imm60 (uint64_t insn_addr, uint64_t val) +{ + ia64_patch(insn_addr, + 0x011ffffe000UL, + ( ((val & 0x0800000000000000UL) >> 23) /* bit 59 -> 36 */ + | ((val & 0x00000000000fffffUL) << 13) /* bit 0 -> 13 */)); + ia64_patch(insn_addr - 1, 0x1fffffffffcUL, val >> 18); +} + +static inline void ia64_imm64 (void *insn, uint64_t val) +{ + /* Ignore the slot number of the relocation; GCC and Intel + toolchains differed for some time on whether IMM64 relocs are + against slot 1 (Intel) or slot 2 (GCC). */ + uint64_t insn_addr = (uint64_t) insn & ~3UL; + + ia64_patch(insn_addr + 2, + 0x01fffefe000UL, + ( ((val & 0x8000000000000000UL) >> 27) /* bit 63 -> 36 */ + | ((val & 0x0000000000200000UL) << 0) /* bit 21 -> 21 */ + | ((val & 0x00000000001f0000UL) << 6) /* bit 16 -> 22 */ + | ((val & 0x000000000000ff80UL) << 20) /* bit 7 -> 27 */ + | ((val & 0x000000000000007fUL) << 13) /* bit 0 -> 13 */) + ); + ia64_patch(insn_addr + 1, 0x1ffffffffffUL, val >> 22); +} + +static inline void ia64_imm60b (void *insn, uint64_t val) +{ + /* Ignore the slot number of the relocation; GCC and Intel + toolchains differed for some time on whether IMM64 relocs are + against slot 1 (Intel) or slot 2 (GCC). */ + uint64_t insn_addr = (uint64_t) insn & ~3UL; + + if (val + ((uint64_t) 1 << 59) >= (1UL << 60)) + fprintf(stderr, "%s: value %ld out of IMM60 range\n", + __FUNCTION__, (int64_t) val); + ia64_patch_imm60(insn_addr + 2, val); +} + +static inline void ia64_imm22 (void *insn, uint64_t val) +{ + if (val + (1 << 21) >= (1 << 22)) + fprintf(stderr, "%s: value %li out of IMM22 range\n", + __FUNCTION__, (int64_t)val); + ia64_patch((uint64_t) insn, 0x01fffcfe000UL, + ( ((val & 0x200000UL) << 15) /* bit 21 -> 36 */ + | ((val & 0x1f0000UL) << 6) /* bit 16 -> 22 */ + | ((val & 0x00ff80UL) << 20) /* bit 7 -> 27 */ + | ((val & 0x00007fUL) << 13) /* bit 0 -> 13 */)); +} + +/* Like ia64_imm22(), but also clear bits 20-21. For addl, this has + the effect of turning "addl rX=imm22,rY" into "addl + rX=imm22,r0". */ +static inline void ia64_imm22_r0 (void *insn, uint64_t val) +{ + if (val + (1 << 21) >= (1 << 22)) + fprintf(stderr, "%s: value %li out of IMM22 range\n", + __FUNCTION__, (int64_t)val); + ia64_patch((uint64_t) insn, 0x01fffcfe000UL | (0x3UL << 20), + ( ((val & 0x200000UL) << 15) /* bit 21 -> 36 */ + | ((val & 0x1f0000UL) << 6) /* bit 16 -> 22 */ + | ((val & 0x00ff80UL) << 20) /* bit 7 -> 27 */ + | ((val & 0x00007fUL) << 13) /* bit 0 -> 13 */)); +} + +static inline void ia64_imm21b (void *insn, uint64_t val) +{ + if (val + (1 << 20) >= (1 << 21)) + fprintf(stderr, "%s: value %li out of IMM21b range\n", + __FUNCTION__, (int64_t)val); + ia64_patch((uint64_t) insn, 0x11ffffe000UL, + ( ((val & 0x100000UL) << 16) /* bit 20 -> 36 */ + | ((val & 0x0fffffUL) << 13) /* bit 0 -> 13 */)); +} + +static inline void ia64_nop_b (void *insn) +{ + ia64_patch((uint64_t) insn, (1UL << 41) - 1, 2UL << 37); +} + +static inline void ia64_ldxmov(void *insn, uint64_t val) +{ + if (val + (1 << 21) < (1 << 22)) + ia64_patch((uint64_t) insn, 0x1fff80fe000UL, 8UL << 37); +} + +static inline int ia64_patch_ltoff(void *insn, uint64_t val, + int relaxable) +{ + if (relaxable && (val + (1 << 21) < (1 << 22))) { + ia64_imm22_r0(insn, val); + return 0; + } + return 1; +} + +struct ia64_fixup { + struct ia64_fixup *next; + void *addr; /* address that needs to be patched */ + long value; +}; + +#define IA64_PLT(insn, plt_index) \ +do { \ + struct ia64_fixup *fixup = alloca(sizeof(*fixup)); \ + fixup->next = plt_fixes; \ + plt_fixes = fixup; \ + fixup->addr = (insn); \ + fixup->value = (plt_index); \ + plt_offset[(plt_index)] = 1; \ +} while (0) + +#define IA64_LTOFF(insn, val, relaxable) \ +do { \ + if (ia64_patch_ltoff(insn, val, relaxable)) { \ + struct ia64_fixup *fixup = alloca(sizeof(*fixup)); \ + fixup->next = ltoff_fixes; \ + ltoff_fixes = fixup; \ + fixup->addr = (insn); \ + fixup->value = (val); \ + } \ +} while (0) + +static inline void ia64_apply_fixes (uint8_t **gen_code_pp, + struct ia64_fixup *ltoff_fixes, + uint64_t gp, + struct ia64_fixup *plt_fixes, + int num_plts, + unsigned long *plt_target, + unsigned int *plt_offset) +{ + static const uint8_t plt_bundle[] = { + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, /* nop 0; movl r1=GP */ + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x60, + + 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, /* nop 0; brl IP */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0 + }; + uint8_t *gen_code_ptr = *gen_code_pp, *plt_start, *got_start, *vp; + struct ia64_fixup *fixup; + unsigned int offset = 0; + struct fdesc { + long ip; + long gp; + } *fdesc; + int i; + + if (plt_fixes) { + plt_start = gen_code_ptr; + + for (i = 0; i < num_plts; ++i) { + if (plt_offset[i]) { + plt_offset[i] = offset; + offset += sizeof(plt_bundle); + + fdesc = (struct fdesc *) plt_target[i]; + memcpy(gen_code_ptr, plt_bundle, sizeof(plt_bundle)); + ia64_imm64 (gen_code_ptr + 0x02, fdesc->gp); + ia64_imm60b(gen_code_ptr + 0x12, + (fdesc->ip - (long) (gen_code_ptr + 0x10)) >> 4); + gen_code_ptr += sizeof(plt_bundle); + } + } + + for (fixup = plt_fixes; fixup; fixup = fixup->next) + ia64_imm21b(fixup->addr, + ((long) plt_start + plt_offset[fixup->value] + - ((long) fixup->addr & ~0xf)) >> 4); + } + + got_start = gen_code_ptr; + + /* First, create the GOT: */ + for (fixup = ltoff_fixes; fixup; fixup = fixup->next) { + /* first check if we already have this value in the GOT: */ + for (vp = got_start; vp < gen_code_ptr; ++vp) + if (*(uint64_t *) vp == fixup->value) + break; + if (vp == gen_code_ptr) { + /* Nope, we need to put the value in the GOT: */ + *(uint64_t *) vp = fixup->value; + gen_code_ptr += 8; + } + ia64_imm22(fixup->addr, (long) vp - gp); + } + /* Keep code ptr aligned. */ + if ((long) gen_code_ptr & 15) + gen_code_ptr += 8; + *gen_code_pp = gen_code_ptr; +} + +#endif diff --git a/tools/ioemu/elf.h b/tools/ioemu/elf.h new file mode 100644 index 0000000000..0dc82e7ca4 --- /dev/null +++ b/tools/ioemu/elf.h @@ -0,0 +1,1160 @@ +#ifndef _QEMU_ELF_H +#define _QEMU_ELF_H + +#include + +/* 32-bit ELF base types. */ +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Off; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Word; + +/* 64-bit ELF base types. */ +typedef uint64_t Elf64_Addr; +typedef uint16_t Elf64_Half; +typedef int16_t Elf64_SHalf; +typedef uint64_t Elf64_Off; +typedef int32_t Elf64_Sword; +typedef uint32_t Elf64_Word; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* These constants are for the segment types stored in the image headers */ +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff +#define PT_MIPS_REGINFO 0x70000000 +#define PT_MIPS_OPTIONS 0x70000001 + +/* Flags in the e_flags field of the header */ +/* MIPS architecture level. */ +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */ + +/* The ABI of a file. */ +#define EF_MIPS_ABI_O32 0x00001000 /* O32 ABI. */ +#define EF_MIPS_ABI_O64 0x00002000 /* O32 extended for 64 bit. */ + +#define EF_MIPS_NOREORDER 0x00000001 +#define EF_MIPS_PIC 0x00000002 +#define EF_MIPS_CPIC 0x00000004 +#define EF_MIPS_ABI2 0x00000020 +#define EF_MIPS_OPTIONS_FIRST 0x00000080 +#define EF_MIPS_32BITMODE 0x00000100 +#define EF_MIPS_ABI 0x0000f000 +#define EF_MIPS_ARCH 0xf0000000 + +/* These constants define the different elf file types */ +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 +#define ET_LOPROC 0xff00 +#define ET_HIPROC 0xffff + +/* These constants define the various ELF target machines */ +#define EM_NONE 0 +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_486 6 /* Perhaps disused */ +#define EM_860 7 + +#define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */ + +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */ + +#define EM_PARISC 15 /* HPPA */ + +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ + +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC64 */ + +#define EM_ARM 40 /* ARM */ + +#define EM_SH 42 /* SuperH */ + +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ + +#define EM_IA_64 50 /* HP/Intel IA-64 */ + +#define EM_X86_64 62 /* AMD x86-64 */ + +#define EM_S390 22 /* IBM S/390 */ + +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ + +#define EM_V850 87 /* NEC v850 */ + +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ + +/* + * This is an interim value that we will use until the committee comes + * up with a final number. + */ +#define EM_ALPHA 0x9026 + +/* Bogus old v850 magic number, used by old tools. */ +#define EM_CYGNUS_V850 0x9080 + +/* + * This is the old interim value for S/390 architecture + */ +#define EM_S390_OLD 0xA390 + +/* This is the info that is needed to parse the dynamic section of the file */ +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_SONAME 14 +#define DT_RPATH 15 +#define DT_SYMBOLIC 16 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_DEBUG 21 +#define DT_TEXTREL 22 +#define DT_JMPREL 23 +#define DT_LOPROC 0x70000000 +#define DT_HIPROC 0x7fffffff +#define DT_MIPS_RLD_VERSION 0x70000001 +#define DT_MIPS_TIME_STAMP 0x70000002 +#define DT_MIPS_ICHECKSUM 0x70000003 +#define DT_MIPS_IVERSION 0x70000004 +#define DT_MIPS_FLAGS 0x70000005 + #define RHF_NONE 0 + #define RHF_HARDWAY 1 + #define RHF_NOTPOT 2 +#define DT_MIPS_BASE_ADDRESS 0x70000006 +#define DT_MIPS_CONFLICT 0x70000008 +#define DT_MIPS_LIBLIST 0x70000009 +#define DT_MIPS_LOCAL_GOTNO 0x7000000a +#define DT_MIPS_CONFLICTNO 0x7000000b +#define DT_MIPS_LIBLISTNO 0x70000010 +#define DT_MIPS_SYMTABNO 0x70000011 +#define DT_MIPS_UNREFEXTNO 0x70000012 +#define DT_MIPS_GOTSYM 0x70000013 +#define DT_MIPS_HIPAGENO 0x70000014 +#define DT_MIPS_RLD_MAP 0x70000016 + +/* This info is needed when parsing the symbol table */ +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 + +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 + +#define ELF_ST_BIND(x) ((x) >> 4) +#define ELF_ST_TYPE(x) (((unsigned int) x) & 0xf) +#define ELF32_ST_BIND(x) ELF_ST_BIND(x) +#define ELF32_ST_TYPE(x) ELF_ST_TYPE(x) +#define ELF64_ST_BIND(x) ELF_ST_BIND(x) +#define ELF64_ST_TYPE(x) ELF_ST_TYPE(x) + +/* Symbolic values for the entries in the auxiliary table + put on the initial stack */ +#define AT_NULL 0 /* end of vector */ +#define AT_IGNORE 1 /* entry should be ignored */ +#define AT_EXECFD 2 /* file descriptor of program */ +#define AT_PHDR 3 /* program headers for program */ +#define AT_PHENT 4 /* size of program header entry */ +#define AT_PHNUM 5 /* number of program headers */ +#define AT_PAGESZ 6 /* system page size */ +#define AT_BASE 7 /* base address of interpreter */ +#define AT_FLAGS 8 /* flags */ +#define AT_ENTRY 9 /* entry point of program */ +#define AT_NOTELF 10 /* program is not ELF */ +#define AT_UID 11 /* real uid */ +#define AT_EUID 12 /* effective uid */ +#define AT_GID 13 /* real gid */ +#define AT_EGID 14 /* effective gid */ +#define AT_PLATFORM 15 /* string identifying CPU for optimizations */ +#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */ +#define AT_CLKTCK 17 /* frequency at which times() increments */ + +typedef struct dynamic{ + Elf32_Sword d_tag; + union{ + Elf32_Sword d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +typedef struct { + Elf64_Sxword d_tag; /* entry tag value */ + union { + Elf64_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +} Elf64_Dyn; + +/* The following are used with relocations */ +#define ELF32_R_SYM(x) ((x) >> 8) +#define ELF32_R_TYPE(x) ((x) & 0xff) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) + +#define R_386_NONE 0 +#define R_386_32 1 +#define R_386_PC32 2 +#define R_386_GOT32 3 +#define R_386_PLT32 4 +#define R_386_COPY 5 +#define R_386_GLOB_DAT 6 +#define R_386_JMP_SLOT 7 +#define R_386_RELATIVE 8 +#define R_386_GOTOFF 9 +#define R_386_GOTPC 10 +#define R_386_NUM 11 + +#define R_MIPS_NONE 0 +#define R_MIPS_16 1 +#define R_MIPS_32 2 +#define R_MIPS_REL32 3 +#define R_MIPS_26 4 +#define R_MIPS_HI16 5 +#define R_MIPS_LO16 6 +#define R_MIPS_GPREL16 7 +#define R_MIPS_LITERAL 8 +#define R_MIPS_GOT16 9 +#define R_MIPS_PC16 10 +#define R_MIPS_CALL16 11 +#define R_MIPS_GPREL32 12 +/* The remaining relocs are defined on Irix, although they are not + in the MIPS ELF ABI. */ +#define R_MIPS_UNUSED1 13 +#define R_MIPS_UNUSED2 14 +#define R_MIPS_UNUSED3 15 +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +/* + * The following two relocation types are specified in the MIPS ABI + * conformance guide version 1.2 but not yet in the psABI. + */ +#define R_MIPS_GOTHI16 22 +#define R_MIPS_GOTLO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +/* + * The following two relocation types are specified in the MIPS ABI + * conformance guide version 1.2 but not yet in the psABI. + */ +#define R_MIPS_CALLHI16 30 +#define R_MIPS_CALLLO16 31 +/* + * This range is reserved for vendor specific relocations. + */ +#define R_MIPS_LOVENDOR 100 +#define R_MIPS_HIVENDOR 127 + + +/* + * Sparc ELF relocation types + */ +#define R_SPARC_NONE 0 +#define R_SPARC_8 1 +#define R_SPARC_16 2 +#define R_SPARC_32 3 +#define R_SPARC_DISP8 4 +#define R_SPARC_DISP16 5 +#define R_SPARC_DISP32 6 +#define R_SPARC_WDISP30 7 +#define R_SPARC_WDISP22 8 +#define R_SPARC_HI22 9 +#define R_SPARC_22 10 +#define R_SPARC_13 11 +#define R_SPARC_LO10 12 +#define R_SPARC_GOT10 13 +#define R_SPARC_GOT13 14 +#define R_SPARC_GOT22 15 +#define R_SPARC_PC10 16 +#define R_SPARC_PC22 17 +#define R_SPARC_WPLT30 18 +#define R_SPARC_COPY 19 +#define R_SPARC_GLOB_DAT 20 +#define R_SPARC_JMP_SLOT 21 +#define R_SPARC_RELATIVE 22 +#define R_SPARC_UA32 23 +#define R_SPARC_PLT32 24 +#define R_SPARC_HIPLT22 25 +#define R_SPARC_LOPLT10 26 +#define R_SPARC_PCPLT32 27 +#define R_SPARC_PCPLT22 28 +#define R_SPARC_PCPLT10 29 +#define R_SPARC_10 30 +#define R_SPARC_11 31 +#define R_SPARC_64 32 +#define R_SPARC_WDISP16 40 +#define R_SPARC_WDISP19 41 +#define R_SPARC_7 43 +#define R_SPARC_5 44 +#define R_SPARC_6 45 + +/* Bits present in AT_HWCAP, primarily for Sparc32. */ + +#define HWCAP_SPARC_FLUSH 1 /* CPU supports flush instruction. */ +#define HWCAP_SPARC_STBAR 2 +#define HWCAP_SPARC_SWAP 4 +#define HWCAP_SPARC_MULDIV 8 +#define HWCAP_SPARC_V9 16 +#define HWCAP_SPARC_ULTRA3 32 + +/* + * 68k ELF relocation types + */ +#define R_68K_NONE 0 +#define R_68K_32 1 +#define R_68K_16 2 +#define R_68K_8 3 +#define R_68K_PC32 4 +#define R_68K_PC16 5 +#define R_68K_PC8 6 +#define R_68K_GOT32 7 +#define R_68K_GOT16 8 +#define R_68K_GOT8 9 +#define R_68K_GOT32O 10 +#define R_68K_GOT16O 11 +#define R_68K_GOT8O 12 +#define R_68K_PLT32 13 +#define R_68K_PLT16 14 +#define R_68K_PLT8 15 +#define R_68K_PLT32O 16 +#define R_68K_PLT16O 17 +#define R_68K_PLT8O 18 +#define R_68K_COPY 19 +#define R_68K_GLOB_DAT 20 +#define R_68K_JMP_SLOT 21 +#define R_68K_RELATIVE 22 + +/* + * Alpha ELF relocation types + */ +#define R_ALPHA_NONE 0 /* No reloc */ +#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ +#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ +#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ +#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ +#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ +#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ +#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ +#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ +#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ +#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ +#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ +#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ +#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ +#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ +#define R_ALPHA_BRSGP 28 +#define R_ALPHA_TLSGD 29 +#define R_ALPHA_TLS_LDM 30 +#define R_ALPHA_DTPMOD64 31 +#define R_ALPHA_GOTDTPREL 32 +#define R_ALPHA_DTPREL64 33 +#define R_ALPHA_DTPRELHI 34 +#define R_ALPHA_DTPRELLO 35 +#define R_ALPHA_DTPREL16 36 +#define R_ALPHA_GOTTPREL 37 +#define R_ALPHA_TPREL64 38 +#define R_ALPHA_TPRELHI 39 +#define R_ALPHA_TPRELLO 40 +#define R_ALPHA_TPREL16 41 + +#define SHF_ALPHA_GPREL 0x10000000 + + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 +/* Keep this the last entry. */ +#define R_PPC_NUM 37 + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_NEW_ABI 0x80 +#define EF_OLD_ABI 0x100 + +/* Additional symbol types for Thumb */ +#define STT_ARM_TFUNC 0xd + +/* ARM-specific values for sh_flags */ +#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ +#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined + in the input to a link step */ + +/* ARM-specific program header flags */ +#define PF_ARM_SB 0x10000000 /* Segment contains the location + addressed by the static base */ + +/* ARM relocs. */ +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* PC relative 26 bit branch */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 +#define R_ARM_THM_PC8 11 +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 +#define R_ARM_THM_SWI8 14 +#define R_ARM_XPC25 15 +#define R_ARM_THM_XPC22 16 +#define R_ARM_COPY 20 /* Copy symbol at runtime */ +#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ +#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ +#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* 32 bit PLT address */ +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ +#define R_ARM_THM_PC9 103 /* thumb conditional branch */ +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 +/* Keep this the last entry. */ +#define R_ARM_NUM 256 + +/* s390 relocations defined by the ABIs */ +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC rel. offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ +#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ +#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ +#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ +#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ +#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ +#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ +#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ +#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ +#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ +#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ +#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ +#define R_390_TLS_GDCALL 38 /* Tag for function call in general + dynamic TLS code. */ +#define R_390_TLS_LDCALL 39 /* Tag for function call in local + dynamic TLS code. */ +#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic + thread local data. */ +#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic + thread local data. */ +#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic + thread local data in LD code. */ +#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic + thread local data in LD code. */ +#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS + block. */ +#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS + block. */ +#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ +#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ +#define R_390_TLS_TPOFF 56 /* Negate offset in static TLS + block. */ +/* Keep this the last entry. */ +#define R_390_NUM 57 + +/* x86-64 relocation types */ +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed pc relative + offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ + +#define R_X86_64_NUM 16 + +/* Legal values for e_flags field of Elf64_Ehdr. */ + +#define EF_ALPHA_32BIT 1 /* All addresses are below 2GB */ + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ +#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ +#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ +#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch + prediction. */ +#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ +#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ + +/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ + +#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ +#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ +#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ + +/* Additional section indeces. */ + +#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared + symbols in ANSI C. */ +#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ +#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ +#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + +/* HPPA relocs. */ + +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ +#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ +#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ +#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ +#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ +#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ +#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ +#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ +#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ +#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ +#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ +#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ +#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ +#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ +#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ +#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ +#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ +#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ +#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ +#define R_PARISC_FPTR64 64 /* 64 bits function address. */ +#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ +#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ +#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ +#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ +#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ +#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ +#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ +#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ +#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ +#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ +#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ +#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ +#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ +#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ +#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ +#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ +#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ +#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ +#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ +#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 /* Copy relocation. */ +#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ +#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ +#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ +#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ +#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ +#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ +#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ +#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ +#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_HIRESERVE 255 + +/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + +/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + +/* IA-64 specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ +#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ +#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ +#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ + +/* Processor specific flags for the Phdr p_flags field. */ +#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ + +/* Processor specific flags for the Shdr sh_flags field. */ +#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ +#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Dyn d_tag field. */ +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + +/* IA-64 relocations. */ +#define R_IA64_NONE 0x00 /* none */ +#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ +#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ +#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ +#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ +#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ +#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ +#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ +#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ +#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ +#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ +#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ +#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ +#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ +#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ +#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ +#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ +#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ +#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ +#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ +#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ +#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ +#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ +#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ +#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ +#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ +#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ +#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ +#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ +#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ +#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ +#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ +#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ +#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ +#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ +#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ +#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ +#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ +#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ +#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ +#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ +#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ +#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ +#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ +#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ +#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ +#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ +#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ +#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ +#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ +#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ +#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ +#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ +#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ +#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ +#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ +#define R_IA64_COPY 0x84 /* copy relocation */ +#define R_IA64_SUB 0x85 /* Addend and symbol difference */ +#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ +#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ +#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ +#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ +#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ +#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ +#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ +#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ +#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ +#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ +#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ +#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ +#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ +#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ +#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ +#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ + +typedef struct elf32_rel { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct elf64_rel { + Elf64_Addr r_offset; /* Location at which to apply the action */ + Elf64_Xword r_info; /* index and type of relocation */ +} Elf64_Rel; + +typedef struct elf32_rela{ + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +typedef struct elf64_rela { + Elf64_Addr r_offset; /* Location at which to apply the action */ + Elf64_Xword r_info; /* index and type of relocation */ + Elf64_Sxword r_addend; /* Constant addend used to compute value */ +} Elf64_Rela; + +typedef struct elf32_sym{ + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +typedef struct elf64_sym { + Elf64_Word st_name; /* Symbol name, index in string tbl */ + unsigned char st_info; /* Type and binding attributes */ + unsigned char st_other; /* No defined meaning, 0 */ + Elf64_Half st_shndx; /* Associated section index */ + Elf64_Addr st_value; /* Value of the symbol */ + Elf64_Xword st_size; /* Associated symbol size */ +} Elf64_Sym; + + +#define EI_NIDENT 16 + +typedef struct elf32_hdr{ + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; /* Entry point */ + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +typedef struct elf64_hdr { + unsigned char e_ident[16]; /* ELF "magic number" */ + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +/* These constants define the permissions on sections in the program + header, p_flags. */ +#define PF_R 0x4 +#define PF_W 0x2 +#define PF_X 0x1 + +typedef struct elf32_phdr{ + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +typedef struct elf64_phdr { + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment, file & memory */ +} Elf64_Phdr; + +/* sh_type */ +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_NUM 12 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff +#define SHT_MIPS_LIST 0x70000000 +#define SHT_MIPS_CONFLICT 0x70000002 +#define SHT_MIPS_GPTAB 0x70000003 +#define SHT_MIPS_UCODE 0x70000004 + +/* sh_flags */ +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 +#define SHF_MIPS_GPREL 0x10000000 + +/* special section indexes */ +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff +#define SHN_MIPS_ACCOMON 0xff00 + +typedef struct elf32_shdr { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +typedef struct elf64_shdr { + Elf64_Word sh_name; /* Section name, index in string tbl */ + Elf64_Word sh_type; /* Type of section */ + Elf64_Xword sh_flags; /* Miscellaneous section attributes */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Size of section in bytes */ + Elf64_Word sh_link; /* Index of another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +#define EI_MAG0 0 /* e_ident[] indexes */ +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_PAD 7 + +#define ELFMAG0 0x7f /* EI_MAG */ +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define ELFCLASSNONE 0 /* EI_CLASS */ +#define ELFCLASS32 1 +#define ELFCLASS64 2 +#define ELFCLASSNUM 3 + +#define ELFDATANONE 0 /* e_ident[EI_DATA] */ +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +#define EV_NONE 0 /* e_version, EI_VERSION */ +#define EV_CURRENT 1 +#define EV_NUM 2 + +/* Notes used in ET_CORE */ +#define NT_PRSTATUS 1 +#define NT_PRFPREG 2 +#define NT_PRPSINFO 3 +#define NT_TASKSTRUCT 4 +#define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ + + +/* Note header in a PT_NOTE section */ +typedef struct elf32_note { + Elf32_Word n_namesz; /* Name size */ + Elf32_Word n_descsz; /* Content size */ + Elf32_Word n_type; /* Content type */ +} Elf32_Nhdr; + +/* Note header in a PT_NOTE section */ +typedef struct elf64_note { + Elf64_Word n_namesz; /* Name size */ + Elf64_Word n_descsz; /* Content size */ + Elf64_Word n_type; /* Content type */ +} Elf64_Nhdr; + +#if ELF_CLASS == ELFCLASS32 + +#define elfhdr elf32_hdr +#define elf_phdr elf32_phdr +#define elf_note elf32_note +#define elf_shdr elf32_shdr +#define elf_sym elf32_sym + +#ifdef ELF_USES_RELOCA +# define ELF_RELOC Elf32_Rela +#else +# define ELF_RELOC Elf32_Rel +#endif + +#else + +#define elfhdr elf64_hdr +#define elf_phdr elf64_phdr +#define elf_note elf64_note +#define elf_shdr elf64_shdr +#define elf_sym elf64_sym + +#ifdef ELF_USES_RELOCA +# define ELF_RELOC Elf64_Rela +#else +# define ELF_RELOC Elf64_Rel +#endif + +#endif /* ELF_CLASS */ + +#ifndef ElfW +# if ELF_CLASS == ELFCLASS32 +# define ElfW(x) Elf32_ ## x +# define ELFW(x) ELF32_ ## x +# else +# define ElfW(x) Elf64_ ## x +# define ELFW(x) ELF64_ ## x +# endif +#endif + + +#endif /* _QEMU_ELF_H */ diff --git a/tools/ioemu/elf_ops.h b/tools/ioemu/elf_ops.h new file mode 100644 index 0000000000..122bf10c1f --- /dev/null +++ b/tools/ioemu/elf_ops.h @@ -0,0 +1,205 @@ +static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr) +{ + bswap16s(&ehdr->e_type); /* Object file type */ + bswap16s(&ehdr->e_machine); /* Architecture */ + bswap32s(&ehdr->e_version); /* Object file version */ + bswapSZs(&ehdr->e_entry); /* Entry point virtual address */ + bswapSZs(&ehdr->e_phoff); /* Program header table file offset */ + bswapSZs(&ehdr->e_shoff); /* Section header table file offset */ + bswap32s(&ehdr->e_flags); /* Processor-specific flags */ + bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ + bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ + bswap16s(&ehdr->e_phnum); /* Program header table entry count */ + bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ + bswap16s(&ehdr->e_shnum); /* Section header table entry count */ + bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ +} + +static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr) +{ + bswap32s(&phdr->p_type); /* Segment type */ + bswapSZs(&phdr->p_offset); /* Segment file offset */ + bswapSZs(&phdr->p_vaddr); /* Segment virtual address */ + bswapSZs(&phdr->p_paddr); /* Segment physical address */ + bswapSZs(&phdr->p_filesz); /* Segment size in file */ + bswapSZs(&phdr->p_memsz); /* Segment size in memory */ + bswap32s(&phdr->p_flags); /* Segment flags */ + bswapSZs(&phdr->p_align); /* Segment alignment */ +} + +static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr) +{ + bswap32s(&shdr->sh_name); + bswap32s(&shdr->sh_type); + bswapSZs(&shdr->sh_flags); + bswapSZs(&shdr->sh_addr); + bswapSZs(&shdr->sh_offset); + bswapSZs(&shdr->sh_size); + bswap32s(&shdr->sh_link); + bswap32s(&shdr->sh_info); + bswapSZs(&shdr->sh_addralign); + bswapSZs(&shdr->sh_entsize); +} + +static void glue(bswap_sym, SZ)(struct elf_sym *sym) +{ + bswap32s(&sym->st_name); + bswapSZs(&sym->st_value); + bswapSZs(&sym->st_size); + bswap16s(&sym->st_shndx); +} + +static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, + int n, int type) +{ + int i; + for(i=0;ie_shoff, + sizeof(struct elf_shdr) * ehdr->e_shnum); + if (!shdr_table) + return -1; + + if (must_swab) { + for (i = 0; i < ehdr->e_shnum; i++) { + glue(bswap_shdr, SZ)(shdr_table + i); + } + } + + symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); + if (!symtab) + goto fail; + syms = load_at(fd, symtab->sh_offset, symtab->sh_size); + if (!syms) + goto fail; + + nsyms = symtab->sh_size / sizeof(struct elf_sym); +#if (SZ == 64) + syms32 = qemu_mallocz(nsyms * sizeof(struct elf32_sym)); +#endif + for (i = 0; i < nsyms; i++) { + if (must_swab) + glue(bswap_sym, SZ)(&syms[i]); +#if (SZ == 64) + syms32[i].st_name = syms[i].st_name; + syms32[i].st_info = syms[i].st_info; + syms32[i].st_other = syms[i].st_other; + syms32[i].st_shndx = syms[i].st_shndx; + syms32[i].st_value = syms[i].st_value & 0xffffffff; + syms32[i].st_size = syms[i].st_size & 0xffffffff; +#endif + } + /* String table */ + if (symtab->sh_link >= ehdr->e_shnum) + goto fail; + strtab = &shdr_table[symtab->sh_link]; + + str = load_at(fd, strtab->sh_offset, strtab->sh_size); + if (!str) + goto fail; + + /* Commit */ + s = qemu_mallocz(sizeof(*s)); +#if (SZ == 64) + s->disas_symtab = syms32; + qemu_free(syms); +#else + s->disas_symtab = syms; +#endif + s->disas_num_syms = nsyms; + s->disas_strtab = str; + s->next = syminfos; + syminfos = s; + qemu_free(shdr_table); + return 0; + fail: +#if (SZ == 64) + qemu_free(syms32); +#endif + qemu_free(syms); + qemu_free(str); + qemu_free(shdr_table); + return -1; +} + +int glue(load_elf, SZ)(int fd, int64_t virt_to_phys_addend, + int must_swab, uint64_t *pentry) +{ + struct elfhdr ehdr; + struct elf_phdr *phdr = NULL, *ph; + int size, i, total_size; + elf_word mem_size, addr; + uint8_t *data = NULL; + + if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) + goto fail; + if (must_swab) { + glue(bswap_ehdr, SZ)(&ehdr); + } + + if (pentry) + *pentry = (uint64_t)ehdr.e_entry; + + glue(load_symbols, SZ)(&ehdr, fd, must_swab); + + size = ehdr.e_phnum * sizeof(phdr[0]); + lseek(fd, ehdr.e_phoff, SEEK_SET); + phdr = qemu_mallocz(size); + if (!phdr) + goto fail; + if (read(fd, phdr, size) != size) + goto fail; + if (must_swab) { + for(i = 0; i < ehdr.e_phnum; i++) { + ph = &phdr[i]; + glue(bswap_phdr, SZ)(ph); + } + } + + total_size = 0; + for(i = 0; i < ehdr.e_phnum; i++) { + ph = &phdr[i]; + if (ph->p_type == PT_LOAD) { + mem_size = ph->p_memsz; + /* XXX: avoid allocating */ + data = qemu_mallocz(mem_size); + if (ph->p_filesz > 0) { + if (lseek(fd, ph->p_offset, SEEK_SET) < 0) + goto fail; + if (read(fd, data, ph->p_filesz) != ph->p_filesz) + goto fail; + } + addr = ph->p_vaddr + virt_to_phys_addend; + + cpu_physical_memory_write_rom(addr, data, mem_size); + + total_size += mem_size; + + qemu_free(data); + data = NULL; + } + } + qemu_free(phdr); + return total_size; + fail: + qemu_free(data); + qemu_free(phdr); + return -1; +} + diff --git a/tools/ioemu/exec-all.h b/tools/ioemu/exec-all.h new file mode 100644 index 0000000000..adebf0d791 --- /dev/null +++ b/tools/ioemu/exec-all.h @@ -0,0 +1,605 @@ +/* + * internal execution defines for qemu + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* allow to see translation results - the slowdown should be negligible, so we leave it */ +#define DEBUG_DISAS + +#ifndef glue +#define xglue(x, y) x ## y +#define glue(x, y) xglue(x, y) +#define stringify(s) tostring(s) +#define tostring(s) #s +#endif + +#if __GNUC__ < 3 +#define __builtin_expect(x, n) (x) +#endif + +#ifdef __i386__ +#define REGPARM(n) __attribute((regparm(n))) +#else +#define REGPARM(n) +#endif + +/* is_jmp field values */ +#define DISAS_NEXT 0 /* next instruction can be analyzed */ +#define DISAS_JUMP 1 /* only pc was modified dynamically */ +#define DISAS_UPDATE 2 /* cpu state was modified dynamically */ +#define DISAS_TB_JUMP 3 /* only pc was modified statically */ + +struct TranslationBlock; + +/* XXX: make safe guess about sizes */ +#define MAX_OP_PER_INSTR 32 +#define OPC_BUF_SIZE 512 +#define OPC_MAX_SIZE (OPC_BUF_SIZE - MAX_OP_PER_INSTR) + +#define OPPARAM_BUF_SIZE (OPC_BUF_SIZE * 3) + +extern uint16_t gen_opc_buf[OPC_BUF_SIZE]; +extern uint32_t gen_opparam_buf[OPPARAM_BUF_SIZE]; +extern long gen_labels[OPC_BUF_SIZE]; +extern int nb_gen_labels; +extern target_ulong gen_opc_pc[OPC_BUF_SIZE]; +extern target_ulong gen_opc_npc[OPC_BUF_SIZE]; +extern uint8_t gen_opc_cc_op[OPC_BUF_SIZE]; +extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE]; +extern target_ulong gen_opc_jump_pc[2]; +extern uint32_t gen_opc_hflags[OPC_BUF_SIZE]; + +typedef void (GenOpFunc)(void); +typedef void (GenOpFunc1)(long); +typedef void (GenOpFunc2)(long, long); +typedef void (GenOpFunc3)(long, long, long); + +#if defined(TARGET_I386) + +void optimize_flags_init(void); + +#endif + +extern FILE *logfile; +extern int loglevel; + +int gen_intermediate_code(CPUState *env, struct TranslationBlock *tb); +int gen_intermediate_code_pc(CPUState *env, struct TranslationBlock *tb); +void dump_ops(const uint16_t *opc_buf, const uint32_t *opparam_buf); +int cpu_gen_code(CPUState *env, struct TranslationBlock *tb, + int max_code_size, int *gen_code_size_ptr); +int cpu_restore_state(struct TranslationBlock *tb, + CPUState *env, unsigned long searched_pc, + void *puc); +int cpu_gen_code_copy(CPUState *env, struct TranslationBlock *tb, + int max_code_size, int *gen_code_size_ptr); +int cpu_restore_state_copy(struct TranslationBlock *tb, + CPUState *env, unsigned long searched_pc, + void *puc); +void cpu_resume_from_signal(CPUState *env1, void *puc); +void cpu_exec_init(CPUState *env); +int page_unprotect(target_ulong address, unsigned long pc, void *puc); +void tb_invalidate_phys_page_range(target_ulong start, target_ulong end, + int is_cpu_write_access); +void tb_invalidate_page_range(target_ulong start, target_ulong end); +void tlb_flush_page(CPUState *env, target_ulong addr); +void tlb_flush(CPUState *env, int flush_global); +int tlb_set_page_exec(CPUState *env, target_ulong vaddr, + target_phys_addr_t paddr, int prot, + int is_user, int is_softmmu); +static inline int tlb_set_page(CPUState *env, target_ulong vaddr, + target_phys_addr_t paddr, int prot, + int is_user, int is_softmmu) +{ + if (prot & PAGE_READ) + prot |= PAGE_EXEC; + return tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu); +} + +#define CODE_GEN_MAX_SIZE 65536 +#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ + +#define CODE_GEN_PHYS_HASH_BITS 15 +#define CODE_GEN_PHYS_HASH_SIZE (1 << CODE_GEN_PHYS_HASH_BITS) + +/* maximum total translate dcode allocated */ + +/* NOTE: the translated code area cannot be too big because on some + archs the range of "fast" function calls is limited. Here is a + summary of the ranges: + + i386 : signed 32 bits + arm : signed 26 bits + ppc : signed 24 bits + sparc : signed 32 bits + alpha : signed 23 bits +*/ + +#if defined(__alpha__) +#define CODE_GEN_BUFFER_SIZE (2 * 1024 * 1024) +#elif defined(__ia64) +#define CODE_GEN_BUFFER_SIZE (4 * 1024 * 1024) /* range of addl */ +#elif defined(__powerpc__) +#define CODE_GEN_BUFFER_SIZE (6 * 1024 * 1024) +#else +#define CODE_GEN_BUFFER_SIZE (16 * 1024 * 1024) +#endif + +//#define CODE_GEN_BUFFER_SIZE (128 * 1024) + +/* estimated block size for TB allocation */ +/* XXX: use a per code average code fragment size and modulate it + according to the host CPU */ +#if defined(CONFIG_SOFTMMU) +#define CODE_GEN_AVG_BLOCK_SIZE 128 +#else +#define CODE_GEN_AVG_BLOCK_SIZE 64 +#endif + +#define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / CODE_GEN_AVG_BLOCK_SIZE) + +#if defined(__powerpc__) +#define USE_DIRECT_JUMP +#endif +#if defined(__i386__) && !defined(_WIN32) +#define USE_DIRECT_JUMP +#endif + +typedef struct TranslationBlock { + target_ulong pc; /* simulated PC corresponding to this block (EIP + CS base) */ + target_ulong cs_base; /* CS base for this block */ + unsigned int flags; /* flags defining in which context the code was generated */ + uint16_t size; /* size of target code for this block (1 <= + size <= TARGET_PAGE_SIZE) */ + uint16_t cflags; /* compile flags */ +#define CF_CODE_COPY 0x0001 /* block was generated in code copy mode */ +#define CF_TB_FP_USED 0x0002 /* fp ops are used in the TB */ +#define CF_FP_USED 0x0004 /* fp ops are used in the TB or in a chained TB */ +#define CF_SINGLE_INSN 0x0008 /* compile only a single instruction */ + + uint8_t *tc_ptr; /* pointer to the translated code */ + /* next matching tb for physical address. */ + struct TranslationBlock *phys_hash_next; + /* first and second physical page containing code. The lower bit + of the pointer tells the index in page_next[] */ + struct TranslationBlock *page_next[2]; + target_ulong page_addr[2]; + + /* the following data are used to directly call another TB from + the code of this one. */ + uint16_t tb_next_offset[2]; /* offset of original jump target */ +#ifdef USE_DIRECT_JUMP + uint16_t tb_jmp_offset[4]; /* offset of jump instruction */ +#else + uint32_t tb_next[2]; /* address of jump generated code */ +#endif + /* list of TBs jumping to this one. This is a circular list using + the two least significant bits of the pointers to tell what is + the next pointer: 0 = jmp_next[0], 1 = jmp_next[1], 2 = + jmp_first */ + struct TranslationBlock *jmp_next[2]; + struct TranslationBlock *jmp_first; +} TranslationBlock; + +static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc) +{ + return (pc ^ (pc >> TB_JMP_CACHE_BITS)) & (TB_JMP_CACHE_SIZE - 1); +} + +static inline unsigned int tb_phys_hash_func(unsigned long pc) +{ + return pc & (CODE_GEN_PHYS_HASH_SIZE - 1); +} + +TranslationBlock *tb_alloc(target_ulong pc); +void tb_flush(CPUState *env); +void tb_link_phys(TranslationBlock *tb, + target_ulong phys_pc, target_ulong phys_page2); + +extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; + +extern uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; +extern uint8_t *code_gen_ptr; + +#if defined(USE_DIRECT_JUMP) + +#if defined(__powerpc__) +static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr) +{ + uint32_t val, *ptr; + + /* patch the branch destination */ + ptr = (uint32_t *)jmp_addr; + val = *ptr; + val = (val & ~0x03fffffc) | ((addr - jmp_addr) & 0x03fffffc); + *ptr = val; + /* flush icache */ + asm volatile ("dcbst 0,%0" : : "r"(ptr) : "memory"); + asm volatile ("sync" : : : "memory"); + asm volatile ("icbi 0,%0" : : "r"(ptr) : "memory"); + asm volatile ("sync" : : : "memory"); + asm volatile ("isync" : : : "memory"); +} +#elif defined(__i386__) +static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr) +{ + /* patch the branch destination */ + *(uint32_t *)jmp_addr = addr - (jmp_addr + 4); + /* no need to flush icache explicitely */ +} +#endif + +static inline void tb_set_jmp_target(TranslationBlock *tb, + int n, unsigned long addr) +{ + unsigned long offset; + + offset = tb->tb_jmp_offset[n]; + tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr); + offset = tb->tb_jmp_offset[n + 2]; + if (offset != 0xffff) + tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr); +} + +#else + +/* set the jump target */ +static inline void tb_set_jmp_target(TranslationBlock *tb, + int n, unsigned long addr) +{ + tb->tb_next[n] = addr; +} + +#endif + +static inline void tb_add_jump(TranslationBlock *tb, int n, + TranslationBlock *tb_next) +{ + /* NOTE: this test is only needed for thread safety */ + if (!tb->jmp_next[n]) { + /* patch the native jump address */ + tb_set_jmp_target(tb, n, (unsigned long)tb_next->tc_ptr); + + /* add in TB jmp circular list */ + tb->jmp_next[n] = tb_next->jmp_first; + tb_next->jmp_first = (TranslationBlock *)((long)(tb) | (n)); + } +} + +TranslationBlock *tb_find_pc(unsigned long pc_ptr); + +#ifndef offsetof +#define offsetof(type, field) ((size_t) &((type *)0)->field) +#endif + +#if defined(_WIN32) +#define ASM_DATA_SECTION ".section \".data\"\n" +#define ASM_PREVIOUS_SECTION ".section .text\n" +#elif defined(__APPLE__) +#define ASM_DATA_SECTION ".data\n" +#define ASM_PREVIOUS_SECTION ".text\n" +#else +#define ASM_DATA_SECTION ".section \".data\"\n" +#define ASM_PREVIOUS_SECTION ".previous\n" +#endif + +#define ASM_OP_LABEL_NAME(n, opname) \ + ASM_NAME(__op_label) #n "." ASM_NAME(opname) + +#if defined(__powerpc__) + +/* we patch the jump instruction directly */ +#define GOTO_TB(opname, tbparam, n)\ +do {\ + asm volatile (ASM_DATA_SECTION\ + ASM_OP_LABEL_NAME(n, opname) ":\n"\ + ".long 1f\n"\ + ASM_PREVIOUS_SECTION \ + "b " ASM_NAME(__op_jmp) #n "\n"\ + "1:\n");\ +} while (0) + +#elif defined(__i386__) && defined(USE_DIRECT_JUMP) + +/* we patch the jump instruction directly */ +#define GOTO_TB(opname, tbparam, n)\ +do {\ + asm volatile (".section .data\n"\ + ASM_OP_LABEL_NAME(n, opname) ":\n"\ + ".long 1f\n"\ + ASM_PREVIOUS_SECTION \ + "jmp " ASM_NAME(__op_jmp) #n "\n"\ + "1:\n");\ +} while (0) + +#else + +/* jump to next block operations (more portable code, does not need + cache flushing, but slower because of indirect jump) */ +#define GOTO_TB(opname, tbparam, n)\ +do {\ + static void __attribute__((unused)) *dummy ## n = &&dummy_label ## n;\ + static void __attribute__((unused)) *__op_label ## n \ + __asm__(ASM_OP_LABEL_NAME(n, opname)) = &&label ## n;\ + goto *(void *)(((TranslationBlock *)tbparam)->tb_next[n]);\ +label ## n: ;\ +dummy_label ## n: ;\ +} while (0) + +#endif + +extern CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; +extern CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4]; +extern void *io_mem_opaque[IO_MEM_NB_ENTRIES]; + +#ifdef __powerpc__ +static inline int testandset (int *p) +{ + int ret; + __asm__ __volatile__ ( + "0: lwarx %0,0,%1\n" + " xor. %0,%3,%0\n" + " bne 1f\n" + " stwcx. %2,0,%1\n" + " bne- 0b\n" + "1: " + : "=&r" (ret) + : "r" (p), "r" (1), "r" (0) + : "cr0", "memory"); + return ret; +} +#endif + +#ifdef __i386__ +static inline int testandset (int *p) +{ + long int readval = 0; + + __asm__ __volatile__ ("lock; cmpxchgl %2, %0" + : "+m" (*p), "+a" (readval) + : "r" (1) + : "cc"); + return readval; +} +#endif + +#ifdef __x86_64__ +static inline int testandset (int *p) +{ + long int readval = 0; + + __asm__ __volatile__ ("lock; cmpxchgl %2, %0" + : "+m" (*p), "+a" (readval) + : "r" (1) + : "cc"); + return readval; +} +#endif + +#ifdef __s390__ +static inline int testandset (int *p) +{ + int ret; + + __asm__ __volatile__ ("0: cs %0,%1,0(%2)\n" + " jl 0b" + : "=&d" (ret) + : "r" (1), "a" (p), "0" (*p) + : "cc", "memory" ); + return ret; +} +#endif + +#ifdef __alpha__ +static inline int testandset (int *p) +{ + int ret; + unsigned long one; + + __asm__ __volatile__ ("0: mov 1,%2\n" + " ldl_l %0,%1\n" + " stl_c %2,%1\n" + " beq %2,1f\n" + ".subsection 2\n" + "1: br 0b\n" + ".previous" + : "=r" (ret), "=m" (*p), "=r" (one) + : "m" (*p)); + return ret; +} +#endif + +#ifdef __sparc__ +static inline int testandset (int *p) +{ + int ret; + + __asm__ __volatile__("ldstub [%1], %0" + : "=r" (ret) + : "r" (p) + : "memory"); + + return (ret ? 1 : 0); +} +#endif + +#ifdef __arm__ +static inline int testandset (int *spinlock) +{ + register unsigned int ret; + __asm__ __volatile__("swp %0, %1, [%2]" + : "=r"(ret) + : "0"(1), "r"(spinlock)); + + return ret; +} +#endif + +#ifdef __mc68000 +static inline int testandset (int *p) +{ + char ret; + __asm__ __volatile__("tas %1; sne %0" + : "=r" (ret) + : "m" (p) + : "cc","memory"); + return ret; +} +#endif + +#ifdef __ia64 +#include + +static inline int testandset (int *p) +{ + return __sync_lock_test_and_set (p, 1); +} +#endif + +typedef int spinlock_t; + +#define SPIN_LOCK_UNLOCKED 0 + +#if defined(CONFIG_USER_ONLY) +static inline void spin_lock(spinlock_t *lock) +{ + while (testandset(lock)); +} + +static inline void spin_unlock(spinlock_t *lock) +{ + *lock = 0; +} + +static inline int spin_trylock(spinlock_t *lock) +{ + return !testandset(lock); +} +#else +static inline void spin_lock(spinlock_t *lock) +{ +} + +static inline void spin_unlock(spinlock_t *lock) +{ +} + +static inline int spin_trylock(spinlock_t *lock) +{ + return 1; +} +#endif + +extern spinlock_t tb_lock; + +extern int tb_invalidated_flag; + +#if !defined(CONFIG_USER_ONLY) && !defined(CONFIG_DM) + +void tlb_fill(target_ulong addr, int is_write, int is_user, + void *retaddr); + +#define ACCESS_TYPE 3 +#define MEMSUFFIX _code +#define env cpu_single_env + +#define DATA_SIZE 1 +#include "softmmu_header.h" + +#define DATA_SIZE 2 +#include "softmmu_header.h" + +#define DATA_SIZE 4 +#include "softmmu_header.h" + +#define DATA_SIZE 8 +#include "softmmu_header.h" + +#undef ACCESS_TYPE +#undef MEMSUFFIX +#undef env + +#endif + +#if defined(CONFIG_USER_ONLY) || defined(CONFIG_DM) +static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr) +{ + return addr; +} +#else +/* NOTE: this function can trigger an exception */ +/* NOTE2: the returned address is not exactly the physical address: it + is the offset relative to phys_ram_base */ +static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr) +{ + int is_user, index, pd; + + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); +#if defined(TARGET_I386) + is_user = ((env->hflags & HF_CPL_MASK) == 3); +#elif defined (TARGET_PPC) + is_user = msr_pr; +#elif defined (TARGET_MIPS) + is_user = ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM); +#elif defined (TARGET_SPARC) + is_user = (env->psrs == 0); +#elif defined (TARGET_ARM) + is_user = ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR); +#elif defined (TARGET_SH4) + is_user = ((env->sr & SR_MD) == 0); +#else +#error unimplemented CPU +#endif + if (__builtin_expect(env->tlb_table[is_user][index].addr_code != + (addr & TARGET_PAGE_MASK), 0)) { + ldub_code(addr); + } + pd = env->tlb_table[is_user][index].addr_code & ~TARGET_PAGE_MASK; + if (pd > IO_MEM_ROM) { + cpu_abort(env, "Trying to execute code outside RAM or ROM at 0x%08lx\n", addr); + } + return addr + env->tlb_table[is_user][index].addend - (unsigned long)phys_ram_base; +} +#endif + + +#ifdef USE_KQEMU +#define KQEMU_MODIFY_PAGE_MASK (0xff & ~(VGA_DIRTY_FLAG | CODE_DIRTY_FLAG)) + +int kqemu_init(CPUState *env); +int kqemu_cpu_exec(CPUState *env); +void kqemu_flush_page(CPUState *env, target_ulong addr); +void kqemu_flush(CPUState *env, int global); +void kqemu_set_notdirty(CPUState *env, ram_addr_t ram_addr); +void kqemu_modify_page(CPUState *env, ram_addr_t ram_addr); +void kqemu_cpu_interrupt(CPUState *env); +void kqemu_record_dump(void); + +static inline int kqemu_is_ok(CPUState *env) +{ + return(env->kqemu_enabled && + (env->cr[0] & CR0_PE_MASK) && + !(env->hflags & HF_INHIBIT_IRQ_MASK) && + (env->eflags & IF_MASK) && + !(env->eflags & VM_MASK) && + (env->kqemu_enabled == 2 || + ((env->hflags & HF_CPL_MASK) == 3 && + (env->eflags & IOPL_MASK) != IOPL_MASK))); +} + +#endif diff --git a/tools/ioemu/exec.c b/tools/ioemu/exec.c new file mode 100644 index 0000000000..f900e09f00 --- /dev/null +++ b/tools/ioemu/exec.c @@ -0,0 +1,2379 @@ +/* + * virtual page mapping and translated block handling + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "config.h" +#ifdef _WIN32 +#include +#else +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "exec-all.h" +#if defined(CONFIG_USER_ONLY) +#include +#endif + +//#define DEBUG_TB_INVALIDATE +//#define DEBUG_FLUSH +//#define DEBUG_TLB + +/* make various TB consistency checks */ +//#define DEBUG_TB_CHECK +//#define DEBUG_TLB_CHECK + +#if !defined(CONFIG_USER_ONLY) +/* TB consistency checks only implemented for usermode emulation. */ +#undef DEBUG_TB_CHECK +#endif + +/* threshold to flush the translated code buffer */ +#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE) + +#define SMC_BITMAP_USE_THRESHOLD 10 + +#define MMAP_AREA_START 0x00000000 +#define MMAP_AREA_END 0xa8000000 + +#if defined(TARGET_SPARC64) +#define TARGET_PHYS_ADDR_SPACE_BITS 41 +#elif defined(TARGET_PPC64) +#define TARGET_PHYS_ADDR_SPACE_BITS 42 +#else +/* Note: for compatibility with kqemu, we use 32 bits for x86_64 */ +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#endif + +TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; +TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; +int nb_tbs; +/* any access to the tbs or the page table must use this lock */ +spinlock_t tb_lock = SPIN_LOCK_UNLOCKED; + +uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE] __attribute__((aligned (32))); +uint8_t *code_gen_ptr; + +int phys_ram_size; +int phys_ram_fd; +uint8_t *phys_ram_base; +uint8_t *phys_ram_dirty; + +CPUState *first_cpu; +/* current CPU in the current thread. It is only valid inside + cpu_exec() */ +CPUState *cpu_single_env; + +typedef struct PageDesc { + /* list of TBs intersecting this ram page */ + TranslationBlock *first_tb; + /* in order to optimize self modifying code, we count the number + of lookups we do to a given page to use a bitmap */ + unsigned int code_write_count; + uint8_t *code_bitmap; +#if defined(CONFIG_USER_ONLY) + unsigned long flags; +#endif +} PageDesc; + +typedef struct PhysPageDesc { + /* offset in host memory of the page + io_index in the low 12 bits */ + uint32_t phys_offset; +} PhysPageDesc; + +#define L2_BITS 10 +#define L1_BITS (32 - L2_BITS - TARGET_PAGE_BITS) + +#define L1_SIZE (1 << L1_BITS) +#define L2_SIZE (1 << L2_BITS) + +static void io_mem_init(void); + +unsigned long qemu_real_host_page_size; +unsigned long qemu_host_page_bits; +unsigned long qemu_host_page_size; +unsigned long qemu_host_page_mask; + +/* XXX: for system emulation, it could just be an array */ +static PageDesc *l1_map[L1_SIZE]; +PhysPageDesc **l1_phys_map; + +/* io memory support */ +CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; +CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4]; +void *io_mem_opaque[IO_MEM_NB_ENTRIES]; +static int io_mem_nb; + +/* log support */ +char *logfilename = "/tmp/qemu.log"; +FILE *logfile; +int loglevel; + +/* statistics */ +static int tlb_flush_count; +static int tb_flush_count; +static int tb_phys_invalidate_count; + +static void page_init(void) +{ + /* NOTE: we can always suppose that qemu_host_page_size >= + TARGET_PAGE_SIZE */ +#ifdef _WIN32 + { + SYSTEM_INFO system_info; + DWORD old_protect; + + GetSystemInfo(&system_info); + qemu_real_host_page_size = system_info.dwPageSize; + + VirtualProtect(code_gen_buffer, sizeof(code_gen_buffer), + PAGE_EXECUTE_READWRITE, &old_protect); + } +#else + qemu_real_host_page_size = getpagesize(); + { + unsigned long start, end; + + start = (unsigned long)code_gen_buffer; + start &= ~(qemu_real_host_page_size - 1); + + end = (unsigned long)code_gen_buffer + sizeof(code_gen_buffer); + end += qemu_real_host_page_size - 1; + end &= ~(qemu_real_host_page_size - 1); + + mprotect((void *)start, end - start, + PROT_READ | PROT_WRITE | PROT_EXEC); + } +#endif + + if (qemu_host_page_size == 0) + qemu_host_page_size = qemu_real_host_page_size; + if (qemu_host_page_size < TARGET_PAGE_SIZE) + qemu_host_page_size = TARGET_PAGE_SIZE; + qemu_host_page_bits = 0; + while ((1 << qemu_host_page_bits) < qemu_host_page_size) + qemu_host_page_bits++; + qemu_host_page_mask = ~(qemu_host_page_size - 1); + l1_phys_map = qemu_vmalloc(L1_SIZE * sizeof(void *)); + memset(l1_phys_map, 0, L1_SIZE * sizeof(void *)); +} + +static inline PageDesc *page_find_alloc(unsigned int index) +{ + PageDesc **lp, *p; + + lp = &l1_map[index >> L2_BITS]; + p = *lp; + if (!p) { + /* allocate if not found */ + p = qemu_malloc(sizeof(PageDesc) * L2_SIZE); + memset(p, 0, sizeof(PageDesc) * L2_SIZE); + *lp = p; + } + return p + (index & (L2_SIZE - 1)); +} + +static inline PageDesc *page_find(unsigned int index) +{ + PageDesc *p; + + p = l1_map[index >> L2_BITS]; + if (!p) + return 0; + return p + (index & (L2_SIZE - 1)); +} + +static PhysPageDesc *phys_page_find_alloc(target_phys_addr_t index, int alloc) +{ + void **lp, **p; + PhysPageDesc *pd; + + p = (void **)l1_phys_map; +#if TARGET_PHYS_ADDR_SPACE_BITS > 32 + +#if TARGET_PHYS_ADDR_SPACE_BITS > (32 + L1_BITS) +#error unsupported TARGET_PHYS_ADDR_SPACE_BITS +#endif + lp = p + ((index >> (L1_BITS + L2_BITS)) & (L1_SIZE - 1)); + p = *lp; + if (!p) { + /* allocate if not found */ + if (!alloc) + return NULL; + p = qemu_vmalloc(sizeof(void *) * L1_SIZE); + memset(p, 0, sizeof(void *) * L1_SIZE); + *lp = p; + } +#endif + lp = p + ((index >> L2_BITS) & (L1_SIZE - 1)); + pd = *lp; + if (!pd) { + int i; + /* allocate if not found */ + if (!alloc) + return NULL; + pd = qemu_vmalloc(sizeof(PhysPageDesc) * L2_SIZE); + *lp = pd; + for (i = 0; i < L2_SIZE; i++) + pd[i].phys_offset = IO_MEM_UNASSIGNED; + } + return ((PhysPageDesc *)pd) + (index & (L2_SIZE - 1)); +} + +static inline PhysPageDesc *phys_page_find(target_phys_addr_t index) +{ + return phys_page_find_alloc(index, 0); +} + +#if !defined(CONFIG_USER_ONLY) +static void tlb_protect_code(ram_addr_t ram_addr); +static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr, + target_ulong vaddr); +#endif + +void cpu_exec_init(CPUState *env) +{ + CPUState **penv; + int cpu_index; + + if (!code_gen_ptr) { + code_gen_ptr = code_gen_buffer; + page_init(); + io_mem_init(); + } + env->next_cpu = NULL; + penv = &first_cpu; + cpu_index = 0; + while (*penv != NULL) { + penv = (CPUState **)&(*penv)->next_cpu; + cpu_index++; + } + env->cpu_index = cpu_index; + *penv = env; +} + +static inline void invalidate_page_bitmap(PageDesc *p) +{ + if (p->code_bitmap) { + qemu_free(p->code_bitmap); + p->code_bitmap = NULL; + } + p->code_write_count = 0; +} + +/* set to NULL all the 'first_tb' fields in all PageDescs */ +static void page_flush_tb(void) +{ + int i, j; + PageDesc *p; + + for(i = 0; i < L1_SIZE; i++) { + p = l1_map[i]; + if (p) { + for(j = 0; j < L2_SIZE; j++) { + p->first_tb = NULL; + invalidate_page_bitmap(p); + p++; + } + } + } +} + +/* flush all the translation blocks */ +/* XXX: tb_flush is currently not thread safe */ +void tb_flush(CPUState *env1) +{ + CPUState *env; +#if defined(DEBUG_FLUSH) + printf("qemu: flush code_size=%d nb_tbs=%d avg_tb_size=%d\n", + code_gen_ptr - code_gen_buffer, + nb_tbs, + nb_tbs > 0 ? (code_gen_ptr - code_gen_buffer) / nb_tbs : 0); +#endif + nb_tbs = 0; + + for(env = first_cpu; env != NULL; env = env->next_cpu) { + memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *)); + } + + memset (tb_phys_hash, 0, CODE_GEN_PHYS_HASH_SIZE * sizeof (void *)); + page_flush_tb(); + + code_gen_ptr = code_gen_buffer; + /* XXX: flush processor icache at this point if cache flush is + expensive */ + tb_flush_count++; +} + +#ifdef DEBUG_TB_CHECK + +static void tb_invalidate_check(unsigned long address) +{ + TranslationBlock *tb; + int i; + address &= TARGET_PAGE_MASK; + for(i = 0;i < CODE_GEN_PHYS_HASH_SIZE; i++) { + for(tb = tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) { + if (!(address + TARGET_PAGE_SIZE <= tb->pc || + address >= tb->pc + tb->size)) { + printf("ERROR invalidate: address=%08lx PC=%08lx size=%04x\n", + address, (long)tb->pc, tb->size); + } + } + } +} + +/* verify that all the pages have correct rights for code */ +static void tb_page_check(void) +{ + TranslationBlock *tb; + int i, flags1, flags2; + + for(i = 0;i < CODE_GEN_PHYS_HASH_SIZE; i++) { + for(tb = tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) { + flags1 = page_get_flags(tb->pc); + flags2 = page_get_flags(tb->pc + tb->size - 1); + if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) { + printf("ERROR page flags: PC=%08lx size=%04x f1=%x f2=%x\n", + (long)tb->pc, tb->size, flags1, flags2); + } + } + } +} + +void tb_jmp_check(TranslationBlock *tb) +{ + TranslationBlock *tb1; + unsigned int n1; + + /* suppress any remaining jumps to this TB */ + tb1 = tb->jmp_first; + for(;;) { + n1 = (long)tb1 & 3; + tb1 = (TranslationBlock *)((long)tb1 & ~3); + if (n1 == 2) + break; + tb1 = tb1->jmp_next[n1]; + } + /* check end of list */ + if (tb1 != tb) { + printf("ERROR: jmp_list from 0x%08lx\n", (long)tb); + } +} + +#endif + +/* invalidate one TB */ +static inline void tb_remove(TranslationBlock **ptb, TranslationBlock *tb, + int next_offset) +{ + TranslationBlock *tb1; + for(;;) { + tb1 = *ptb; + if (tb1 == tb) { + *ptb = *(TranslationBlock **)((char *)tb1 + next_offset); + break; + } + ptb = (TranslationBlock **)((char *)tb1 + next_offset); + } +} + +static inline void tb_page_remove(TranslationBlock **ptb, TranslationBlock *tb) +{ + TranslationBlock *tb1; + unsigned int n1; + + for(;;) { + tb1 = *ptb; + n1 = (long)tb1 & 3; + tb1 = (TranslationBlock *)((long)tb1 & ~3); + if (tb1 == tb) { + *ptb = tb1->page_next[n1]; + break; + } + ptb = &tb1->page_next[n1]; + } +} + +static inline void tb_jmp_remove(TranslationBlock *tb, int n) +{ + TranslationBlock *tb1, **ptb; + unsigned int n1; + + ptb = &tb->jmp_next[n]; + tb1 = *ptb; + if (tb1) { + /* find tb(n) in circular list */ + for(;;) { + tb1 = *ptb; + n1 = (long)tb1 & 3; + tb1 = (TranslationBlock *)((long)tb1 & ~3); + if (n1 == n && tb1 == tb) + break; + if (n1 == 2) { + ptb = &tb1->jmp_first; + } else { + ptb = &tb1->jmp_next[n1]; + } + } + /* now we can suppress tb(n) from the list */ + *ptb = tb->jmp_next[n]; + + tb->jmp_next[n] = NULL; + } +} + +/* reset the jump entry 'n' of a TB so that it is not chained to + another TB */ +static inline void tb_reset_jump(TranslationBlock *tb, int n) +{ + tb_set_jmp_target(tb, n, (unsigned long)(tb->tc_ptr + tb->tb_next_offset[n])); +} + +static inline void tb_phys_invalidate(TranslationBlock *tb, unsigned int page_addr) +{ + CPUState *env; + PageDesc *p; + unsigned int h, n1; + target_ulong phys_pc; + TranslationBlock *tb1, *tb2; + + /* remove the TB from the hash list */ + phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK); + h = tb_phys_hash_func(phys_pc); + tb_remove(&tb_phys_hash[h], tb, + offsetof(TranslationBlock, phys_hash_next)); + + /* remove the TB from the page list */ + if (tb->page_addr[0] != page_addr) { + p = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS); + tb_page_remove(&p->first_tb, tb); + invalidate_page_bitmap(p); + } + if (tb->page_addr[1] != -1 && tb->page_addr[1] != page_addr) { + p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS); + tb_page_remove(&p->first_tb, tb); + invalidate_page_bitmap(p); + } + + tb_invalidated_flag = 1; + + /* remove the TB from the hash list */ + h = tb_jmp_cache_hash_func(tb->pc); + for(env = first_cpu; env != NULL; env = env->next_cpu) { + if (env->tb_jmp_cache[h] == tb) + env->tb_jmp_cache[h] = NULL; + } + + /* suppress this TB from the two jump lists */ + tb_jmp_remove(tb, 0); + tb_jmp_remove(tb, 1); + + /* suppress any remaining jumps to this TB */ + tb1 = tb->jmp_first; + for(;;) { + n1 = (long)tb1 & 3; + if (n1 == 2) + break; + tb1 = (TranslationBlock *)((long)tb1 & ~3); + tb2 = tb1->jmp_next[n1]; + tb_reset_jump(tb1, n1); + tb1->jmp_next[n1] = NULL; + tb1 = tb2; + } + tb->jmp_first = (TranslationBlock *)((long)tb | 2); /* fail safe */ + + tb_phys_invalidate_count++; +} + +static inline void set_bits(uint8_t *tab, int start, int len) +{ + int end, mask, end1; + + end = start + len; + tab += start >> 3; + mask = 0xff << (start & 7); + if ((start & ~7) == (end & ~7)) { + if (start < end) { + mask &= ~(0xff << (end & 7)); + *tab |= mask; + } + } else { + *tab++ |= mask; + start = (start + 8) & ~7; + end1 = end & ~7; + while (start < end1) { + *tab++ = 0xff; + start += 8; + } + if (start < end) { + mask = ~(0xff << (end & 7)); + *tab |= mask; + } + } +} + +static void build_page_bitmap(PageDesc *p) +{ + int n, tb_start, tb_end; + TranslationBlock *tb; + + p->code_bitmap = qemu_malloc(TARGET_PAGE_SIZE / 8); + if (!p->code_bitmap) + return; + memset(p->code_bitmap, 0, TARGET_PAGE_SIZE / 8); + + tb = p->first_tb; + while (tb != NULL) { + n = (long)tb & 3; + tb = (TranslationBlock *)((long)tb & ~3); + /* NOTE: this is subtle as a TB may span two physical pages */ + if (n == 0) { + /* NOTE: tb_end may be after the end of the page, but + it is not a problem */ + tb_start = tb->pc & ~TARGET_PAGE_MASK; + tb_end = tb_start + tb->size; + if (tb_end > TARGET_PAGE_SIZE) + tb_end = TARGET_PAGE_SIZE; + } else { + tb_start = 0; + tb_end = ((tb->pc + tb->size) & ~TARGET_PAGE_MASK); + } + set_bits(p->code_bitmap, tb_start, tb_end - tb_start); + tb = tb->page_next[n]; + } +} + +#ifdef TARGET_HAS_PRECISE_SMC + +static void tb_gen_code(CPUState *env, + target_ulong pc, target_ulong cs_base, int flags, + int cflags) +{ + TranslationBlock *tb; + uint8_t *tc_ptr; + target_ulong phys_pc, phys_page2, virt_page2; + int code_gen_size; + + phys_pc = get_phys_addr_code(env, pc); + tb = tb_alloc(pc); + if (!tb) { + /* flush must be done */ + tb_flush(env); + /* cannot fail at this point */ + tb = tb_alloc(pc); + } + tc_ptr = code_gen_ptr; + tb->tc_ptr = tc_ptr; + tb->cs_base = cs_base; + tb->flags = flags; + tb->cflags = cflags; + cpu_gen_code(env, tb, CODE_GEN_MAX_SIZE, &code_gen_size); + code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); + + /* check next page if needed */ + virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; + phys_page2 = -1; + if ((pc & TARGET_PAGE_MASK) != virt_page2) { + phys_page2 = get_phys_addr_code(env, virt_page2); + } + tb_link_phys(tb, phys_pc, phys_page2); +} +#endif + +/* invalidate all TBs which intersect with the target physical page + starting in range [start;end[. NOTE: start and end must refer to + the same physical page. 'is_cpu_write_access' should be true if called + from a real cpu write access: the virtual CPU will exit the current + TB if code is modified inside this TB. */ +void tb_invalidate_phys_page_range(target_ulong start, target_ulong end, + int is_cpu_write_access) +{ + int n, current_tb_modified, current_tb_not_found, current_flags; + CPUState *env = cpu_single_env; + PageDesc *p; + TranslationBlock *tb, *tb_next, *current_tb, *saved_tb; + target_ulong tb_start, tb_end; + target_ulong current_pc, current_cs_base; + + p = page_find(start >> TARGET_PAGE_BITS); + if (!p) + return; + if (!p->code_bitmap && + ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD && + is_cpu_write_access) { + /* build code bitmap */ + build_page_bitmap(p); + } + + /* we remove all the TBs in the range [start, end[ */ + /* XXX: see if in some cases it could be faster to invalidate all the code */ + current_tb_not_found = is_cpu_write_access; + current_tb_modified = 0; + current_tb = NULL; /* avoid warning */ + current_pc = 0; /* avoid warning */ + current_cs_base = 0; /* avoid warning */ + current_flags = 0; /* avoid warning */ + tb = p->first_tb; + while (tb != NULL) { + n = (long)tb & 3; + tb = (TranslationBlock *)((long)tb & ~3); + tb_next = tb->page_next[n]; + /* NOTE: this is subtle as a TB may span two physical pages */ + if (n == 0) { + /* NOTE: tb_end may be after the end of the page, but + it is not a problem */ + tb_start = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK); + tb_end = tb_start + tb->size; + } else { + tb_start = tb->page_addr[1]; + tb_end = tb_start + ((tb->pc + tb->size) & ~TARGET_PAGE_MASK); + } + if (!(tb_end <= start || tb_start >= end)) { +#ifdef TARGET_HAS_PRECISE_SMC + if (current_tb_not_found) { + current_tb_not_found = 0; + current_tb = NULL; + if (env->mem_write_pc) { + /* now we have a real cpu fault */ + current_tb = tb_find_pc(env->mem_write_pc); + } + } + if (current_tb == tb && + !(current_tb->cflags & CF_SINGLE_INSN)) { + /* If we are modifying the current TB, we must stop + its execution. We could be more precise by checking + that the modification is after the current PC, but it + would require a specialized function to partially + restore the CPU state */ + + current_tb_modified = 1; + cpu_restore_state(current_tb, env, + env->mem_write_pc, NULL); +#if defined(TARGET_I386) + current_flags = env->hflags; + current_flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK)); + current_cs_base = (target_ulong)env->segs[R_CS].base; + current_pc = current_cs_base + env->eip; +#else +#error unsupported CPU +#endif + } +#endif /* TARGET_HAS_PRECISE_SMC */ + /* we need to do that to handle the case where a signal + occurs while doing tb_phys_invalidate() */ + saved_tb = NULL; + if (env) { + saved_tb = env->current_tb; + env->current_tb = NULL; + } + tb_phys_invalidate(tb, -1); + if (env) { + env->current_tb = saved_tb; + if (env->interrupt_request && env->current_tb) + cpu_interrupt(env, env->interrupt_request); + } + } + tb = tb_next; + } +#if !defined(CONFIG_USER_ONLY) + /* if no code remaining, no need to continue to use slow writes */ + if (!p->first_tb) { + invalidate_page_bitmap(p); + if (is_cpu_write_access) { + tlb_unprotect_code_phys(env, start, env->mem_write_vaddr); + } + } +#endif +#ifdef TARGET_HAS_PRECISE_SMC + if (current_tb_modified) { + /* we generate a block containing just the instruction + modifying the memory. It will ensure that it cannot modify + itself */ + env->current_tb = NULL; + tb_gen_code(env, current_pc, current_cs_base, current_flags, + CF_SINGLE_INSN); + cpu_resume_from_signal(env, NULL); + } +#endif +} + +/* len must be <= 8 and start must be a multiple of len */ +static inline void tb_invalidate_phys_page_fast(target_ulong start, int len) +{ + PageDesc *p; + int offset, b; +#if 0 + if (1) { + if (loglevel) { + fprintf(logfile, "modifying code at 0x%x size=%d EIP=%x PC=%08x\n", + cpu_single_env->mem_write_vaddr, len, + cpu_single_env->eip, + cpu_single_env->eip + (long)cpu_single_env->segs[R_CS].base); + } + } +#endif + p = page_find(start >> TARGET_PAGE_BITS); + if (!p) + return; + if (p->code_bitmap) { + offset = start & ~TARGET_PAGE_MASK; + b = p->code_bitmap[offset >> 3] >> (offset & 7); + if (b & ((1 << len) - 1)) + goto do_invalidate; + } else { + do_invalidate: + tb_invalidate_phys_page_range(start, start + len, 1); + } +} + +#if !defined(CONFIG_SOFTMMU) +static void tb_invalidate_phys_page(target_ulong addr, + unsigned long pc, void *puc) +{ + int n, current_flags, current_tb_modified; + target_ulong current_pc, current_cs_base; + PageDesc *p; + TranslationBlock *tb, *current_tb; +#ifdef TARGET_HAS_PRECISE_SMC + CPUState *env = cpu_single_env; +#endif + + addr &= TARGET_PAGE_MASK; + p = page_find(addr >> TARGET_PAGE_BITS); + if (!p) + return; + tb = p->first_tb; + current_tb_modified = 0; + current_tb = NULL; + current_pc = 0; /* avoid warning */ + current_cs_base = 0; /* avoid warning */ + current_flags = 0; /* avoid warning */ +#ifdef TARGET_HAS_PRECISE_SMC + if (tb && pc != 0) { + current_tb = tb_find_pc(pc); + } +#endif + while (tb != NULL) { + n = (long)tb & 3; + tb = (TranslationBlock *)((long)tb & ~3); +#ifdef TARGET_HAS_PRECISE_SMC + if (current_tb == tb && + !(current_tb->cflags & CF_SINGLE_INSN)) { + /* If we are modifying the current TB, we must stop + its execution. We could be more precise by checking + that the modification is after the current PC, but it + would require a specialized function to partially + restore the CPU state */ + + current_tb_modified = 1; + cpu_restore_state(current_tb, env, pc, puc); +#if defined(TARGET_I386) + current_flags = env->hflags; + current_flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK)); + current_cs_base = (target_ulong)env->segs[R_CS].base; + current_pc = current_cs_base + env->eip; +#else +#error unsupported CPU +#endif + } +#endif /* TARGET_HAS_PRECISE_SMC */ + tb_phys_invalidate(tb, addr); + tb = tb->page_next[n]; + } + p->first_tb = NULL; +#ifdef TARGET_HAS_PRECISE_SMC + if (current_tb_modified) { + /* we generate a block containing just the instruction + modifying the memory. It will ensure that it cannot modify + itself */ + env->current_tb = NULL; + tb_gen_code(env, current_pc, current_cs_base, current_flags, + CF_SINGLE_INSN); + cpu_resume_from_signal(env, puc); + } +#endif +} +#endif + +/* add the tb in the target page and protect it if necessary */ +static inline void tb_alloc_page(TranslationBlock *tb, + unsigned int n, target_ulong page_addr) +{ + PageDesc *p; + TranslationBlock *last_first_tb; + + tb->page_addr[n] = page_addr; + p = page_find_alloc(page_addr >> TARGET_PAGE_BITS); + tb->page_next[n] = p->first_tb; + last_first_tb = p->first_tb; + p->first_tb = (TranslationBlock *)((long)tb | n); + invalidate_page_bitmap(p); + +#if defined(TARGET_HAS_SMC) || 1 + +#if defined(CONFIG_USER_ONLY) + if (p->flags & PAGE_WRITE) { + target_ulong addr; + PageDesc *p2; + int prot; + + /* force the host page as non writable (writes will have a + page fault + mprotect overhead) */ + page_addr &= qemu_host_page_mask; + prot = 0; + for(addr = page_addr; addr < page_addr + qemu_host_page_size; + addr += TARGET_PAGE_SIZE) { + + p2 = page_find (addr >> TARGET_PAGE_BITS); + if (!p2) + continue; + prot |= p2->flags; + p2->flags &= ~PAGE_WRITE; + page_get_flags(addr); + } + mprotect(g2h(page_addr), qemu_host_page_size, + (prot & PAGE_BITS) & ~PAGE_WRITE); +#ifdef DEBUG_TB_INVALIDATE + printf("protecting code page: 0x%08lx\n", + page_addr); +#endif + } +#else + /* if some code is already present, then the pages are already + protected. So we handle the case where only the first TB is + allocated in a physical page */ + if (!last_first_tb) { + tlb_protect_code(page_addr); + } +#endif + +#endif /* TARGET_HAS_SMC */ +} + +/* Allocate a new translation block. Flush the translation buffer if + too many translation blocks or too much generated code. */ +TranslationBlock *tb_alloc(target_ulong pc) +{ + TranslationBlock *tb; + + if (nb_tbs >= CODE_GEN_MAX_BLOCKS || + (code_gen_ptr - code_gen_buffer) >= CODE_GEN_BUFFER_MAX_SIZE) + return NULL; + tb = &tbs[nb_tbs++]; + tb->pc = pc; + tb->cflags = 0; + return tb; +} + +/* add a new TB and link it to the physical page tables. phys_page2 is + (-1) to indicate that only one page contains the TB. */ +void tb_link_phys(TranslationBlock *tb, + target_ulong phys_pc, target_ulong phys_page2) +{ + unsigned int h; + TranslationBlock **ptb; + + /* add in the physical hash table */ + h = tb_phys_hash_func(phys_pc); + ptb = &tb_phys_hash[h]; + tb->phys_hash_next = *ptb; + *ptb = tb; + + /* add in the page list */ + tb_alloc_page(tb, 0, phys_pc & TARGET_PAGE_MASK); + if (phys_page2 != -1) + tb_alloc_page(tb, 1, phys_page2); + else + tb->page_addr[1] = -1; + + tb->jmp_first = (TranslationBlock *)((long)tb | 2); + tb->jmp_next[0] = NULL; + tb->jmp_next[1] = NULL; +#ifdef USE_CODE_COPY + tb->cflags &= ~CF_FP_USED; + if (tb->cflags & CF_TB_FP_USED) + tb->cflags |= CF_FP_USED; +#endif + + /* init original jump addresses */ + if (tb->tb_next_offset[0] != 0xffff) + tb_reset_jump(tb, 0); + if (tb->tb_next_offset[1] != 0xffff) + tb_reset_jump(tb, 1); + +#ifdef DEBUG_TB_CHECK + tb_page_check(); +#endif +} + +/* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr < + tb[1].tc_ptr. Return NULL if not found */ +TranslationBlock *tb_find_pc(unsigned long tc_ptr) +{ + int m_min, m_max, m; + unsigned long v; + TranslationBlock *tb; + + if (nb_tbs <= 0) + return NULL; + if (tc_ptr < (unsigned long)code_gen_buffer || + tc_ptr >= (unsigned long)code_gen_ptr) + return NULL; + /* binary search (cf Knuth) */ + m_min = 0; + m_max = nb_tbs - 1; + while (m_min <= m_max) { + m = (m_min + m_max) >> 1; + tb = &tbs[m]; + v = (unsigned long)tb->tc_ptr; + if (v == tc_ptr) + return tb; + else if (tc_ptr < v) { + m_max = m - 1; + } else { + m_min = m + 1; + } + } + return &tbs[m_max]; +} + +static void tb_reset_jump_recursive(TranslationBlock *tb); + +static inline void tb_reset_jump_recursive2(TranslationBlock *tb, int n) +{ + TranslationBlock *tb1, *tb_next, **ptb; + unsigned int n1; + + tb1 = tb->jmp_next[n]; + if (tb1 != NULL) { + /* find head of list */ + for(;;) { + n1 = (long)tb1 & 3; + tb1 = (TranslationBlock *)((long)tb1 & ~3); + if (n1 == 2) + break; + tb1 = tb1->jmp_next[n1]; + } + /* we are now sure now that tb jumps to tb1 */ + tb_next = tb1; + + /* remove tb from the jmp_first list */ + ptb = &tb_next->jmp_first; + for(;;) { + tb1 = *ptb; + n1 = (long)tb1 & 3; + tb1 = (TranslationBlock *)((long)tb1 & ~3); + if (n1 == n && tb1 == tb) + break; + ptb = &tb1->jmp_next[n1]; + } + *ptb = tb->jmp_next[n]; + tb->jmp_next[n] = NULL; + + /* suppress the jump to next tb in generated code */ + tb_reset_jump(tb, n); + + /* suppress jumps in the tb on which we could have jumped */ + tb_reset_jump_recursive(tb_next); + } +} + +static void tb_reset_jump_recursive(TranslationBlock *tb) +{ + tb_reset_jump_recursive2(tb, 0); + tb_reset_jump_recursive2(tb, 1); +} + +#if defined(TARGET_HAS_ICE) +static void breakpoint_invalidate(CPUState *env, target_ulong pc) +{ + target_ulong addr, pd; + ram_addr_t ram_addr; + PhysPageDesc *p; + + addr = cpu_get_phys_page_debug(env, pc); + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + ram_addr = (pd & TARGET_PAGE_MASK) | (pc & ~TARGET_PAGE_MASK); + tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0); +} +#endif + +/* add a breakpoint. EXCP_DEBUG is returned by the CPU loop if a + breakpoint is reached */ +int cpu_breakpoint_insert(CPUState *env, target_ulong pc) +{ +#if defined(TARGET_HAS_ICE) + int i; + + for(i = 0; i < env->nb_breakpoints; i++) { + if (env->breakpoints[i] == pc) + return 0; + } + + if (env->nb_breakpoints >= MAX_BREAKPOINTS) + return -1; + env->breakpoints[env->nb_breakpoints++] = pc; + + breakpoint_invalidate(env, pc); + return 0; +#else + return -1; +#endif +} + +/* remove a breakpoint */ +int cpu_breakpoint_remove(CPUState *env, target_ulong pc) +{ +#if defined(TARGET_HAS_ICE) + int i; + for(i = 0; i < env->nb_breakpoints; i++) { + if (env->breakpoints[i] == pc) + goto found; + } + return -1; + found: + env->nb_breakpoints--; + if (i < env->nb_breakpoints) + env->breakpoints[i] = env->breakpoints[env->nb_breakpoints]; + + breakpoint_invalidate(env, pc); + return 0; +#else + return -1; +#endif +} + +/* enable or disable single step mode. EXCP_DEBUG is returned by the + CPU loop after each instruction */ +void cpu_single_step(CPUState *env, int enabled) +{ +#if defined(TARGET_HAS_ICE) + if (env->singlestep_enabled != enabled) { + env->singlestep_enabled = enabled; + /* must flush all the translated code to avoid inconsistancies */ + /* XXX: only flush what is necessary */ + tb_flush(env); + } +#endif +} + +/* enable or disable low levels log */ +void cpu_set_log(int log_flags) +{ + loglevel = log_flags; + if (loglevel && !logfile) { + logfile = fopen(logfilename, "w"); + if (!logfile) { + perror(logfilename); + _exit(1); + } +#if !defined(CONFIG_SOFTMMU) + /* must avoid mmap() usage of glibc by setting a buffer "by hand" */ + { + static uint8_t logfile_buf[4096]; + setvbuf(logfile, logfile_buf, _IOLBF, sizeof(logfile_buf)); + } +#else + setvbuf(logfile, NULL, _IOLBF, 0); +#endif + } +} + +void cpu_set_log_filename(const char *filename) +{ + logfilename = strdup(filename); +} + +/* mask must never be zero, except for A20 change call */ +void cpu_interrupt(CPUState *env, int mask) +{ + TranslationBlock *tb; + static int interrupt_lock; + + env->interrupt_request |= mask; + /* if the cpu is currently executing code, we must unlink it and + all the potentially executing TB */ + tb = env->current_tb; + if (tb && !testandset(&interrupt_lock)) { + env->current_tb = NULL; + tb_reset_jump_recursive(tb); + interrupt_lock = 0; + } +} + +void cpu_reset_interrupt(CPUState *env, int mask) +{ + env->interrupt_request &= ~mask; +} + +CPULogItem cpu_log_items[] = { + { CPU_LOG_TB_OUT_ASM, "out_asm", + "show generated host assembly code for each compiled TB" }, + { CPU_LOG_TB_IN_ASM, "in_asm", + "show target assembly code for each compiled TB" }, + { CPU_LOG_TB_OP, "op", + "show micro ops for each compiled TB (only usable if 'in_asm' used)" }, +#ifdef TARGET_I386 + { CPU_LOG_TB_OP_OPT, "op_opt", + "show micro ops after optimization for each compiled TB" }, +#endif + { CPU_LOG_INT, "int", + "show interrupts/exceptions in short format" }, + { CPU_LOG_EXEC, "exec", + "show trace before each executed TB (lots of logs)" }, + { CPU_LOG_TB_CPU, "cpu", + "show CPU state before bloc translation" }, +#ifdef TARGET_I386 + { CPU_LOG_PCALL, "pcall", + "show protected mode far calls/returns/exceptions" }, +#endif +#ifdef DEBUG_IOPORT + { CPU_LOG_IOPORT, "ioport", + "show all i/o ports accesses" }, +#endif + { 0, NULL, NULL }, +}; + +static int cmp1(const char *s1, int n, const char *s2) +{ + if (strlen(s2) != n) + return 0; + return memcmp(s1, s2, n) == 0; +} + +/* takes a comma separated list of log masks. Return 0 if error. */ +int cpu_str_to_log_mask(const char *str) +{ + CPULogItem *item; + int mask; + const char *p, *p1; + + p = str; + mask = 0; + for(;;) { + p1 = strchr(p, ','); + if (!p1) + p1 = p + strlen(p); + if(cmp1(p,p1-p,"all")) { + for(item = cpu_log_items; item->mask != 0; item++) { + mask |= item->mask; + } + } else { + for(item = cpu_log_items; item->mask != 0; item++) { + if (cmp1(p, p1 - p, item->name)) + goto found; + } + return 0; + } + found: + mask |= item->mask; + if (*p1 != ',') + break; + p = p1 + 1; + } + return mask; +} + +void cpu_abort(CPUState *env, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "qemu: fatal: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); +#ifdef TARGET_I386 + cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP); +#else + cpu_dump_state(env, stderr, fprintf, 0); +#endif + va_end(ap); + abort(); +} + +#if !defined(CONFIG_USER_ONLY) + +/* NOTE: if flush_global is true, also flush global entries (not + implemented yet) */ +void tlb_flush(CPUState *env, int flush_global) +{ + int i; + +#if defined(DEBUG_TLB) + printf("tlb_flush:\n"); +#endif + /* must reset current TB so that interrupts cannot modify the + links while we are modifying them */ + env->current_tb = NULL; + + for(i = 0; i < CPU_TLB_SIZE; i++) { + env->tlb_table[0][i].addr_read = -1; + env->tlb_table[0][i].addr_write = -1; + env->tlb_table[0][i].addr_code = -1; + env->tlb_table[1][i].addr_read = -1; + env->tlb_table[1][i].addr_write = -1; + env->tlb_table[1][i].addr_code = -1; + } + + memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *)); + +#if !defined(CONFIG_SOFTMMU) + munmap((void *)MMAP_AREA_START, MMAP_AREA_END - MMAP_AREA_START); +#endif +#ifdef USE_KQEMU + if (env->kqemu_enabled) { + kqemu_flush(env, flush_global); + } +#endif + tlb_flush_count++; +} + +static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) +{ + if (addr == (tlb_entry->addr_read & + (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || + addr == (tlb_entry->addr_write & + (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || + addr == (tlb_entry->addr_code & + (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + tlb_entry->addr_read = -1; + tlb_entry->addr_write = -1; + tlb_entry->addr_code = -1; + } +} + +void tlb_flush_page(CPUState *env, target_ulong addr) +{ + int i; + TranslationBlock *tb; + +#if defined(DEBUG_TLB) + printf("tlb_flush_page: " TARGET_FMT_lx "\n", addr); +#endif + /* must reset current TB so that interrupts cannot modify the + links while we are modifying them */ + env->current_tb = NULL; + + addr &= TARGET_PAGE_MASK; + i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + tlb_flush_entry(&env->tlb_table[0][i], addr); + tlb_flush_entry(&env->tlb_table[1][i], addr); + + for(i = 0; i < TB_JMP_CACHE_SIZE; i++) { + tb = env->tb_jmp_cache[i]; + if (tb && + ((tb->pc & TARGET_PAGE_MASK) == addr || + ((tb->pc + tb->size - 1) & TARGET_PAGE_MASK) == addr)) { + env->tb_jmp_cache[i] = NULL; + } + } + +#if !defined(CONFIG_SOFTMMU) + if (addr < MMAP_AREA_END) + munmap((void *)addr, TARGET_PAGE_SIZE); +#endif +#ifdef USE_KQEMU + if (env->kqemu_enabled) { + kqemu_flush_page(env, addr); + } +#endif +} + +/* update the TLBs so that writes to code in the virtual page 'addr' + can be detected */ +static void tlb_protect_code(ram_addr_t ram_addr) +{ + cpu_physical_memory_reset_dirty(ram_addr, + ram_addr + TARGET_PAGE_SIZE, + CODE_DIRTY_FLAG); +} + +/* update the TLB so that writes in physical page 'phys_addr' are no longer + tested for self modifying code */ +static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr, + target_ulong vaddr) +{ + phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] |= CODE_DIRTY_FLAG; +} + +static inline void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, + unsigned long start, unsigned long length) +{ + unsigned long addr; + if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM) { + addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend; + if ((addr - start) < length) { + tlb_entry->addr_write = (tlb_entry->addr_write & TARGET_PAGE_MASK) | IO_MEM_NOTDIRTY; + } + } +} + +void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, + int dirty_flags) +{ + CPUState *env; + unsigned long length, start1; + int i, mask, len; + uint8_t *p; + + start &= TARGET_PAGE_MASK; + end = TARGET_PAGE_ALIGN(end); + + length = end - start; + if (length == 0) + return; + len = length >> TARGET_PAGE_BITS; +#ifdef USE_KQEMU + /* XXX: should not depend on cpu context */ + env = first_cpu; + if (env->kqemu_enabled) { + ram_addr_t addr; + addr = start; + for(i = 0; i < len; i++) { + kqemu_set_notdirty(env, addr); + addr += TARGET_PAGE_SIZE; + } + } +#endif + mask = ~dirty_flags; + p = phys_ram_dirty + (start >> TARGET_PAGE_BITS); + for(i = 0; i < len; i++) + p[i] &= mask; + + /* we modify the TLB cache so that the dirty bit will be set again + when accessing the range */ + start1 = start + (unsigned long)phys_ram_base; + for(env = first_cpu; env != NULL; env = env->next_cpu) { + for(i = 0; i < CPU_TLB_SIZE; i++) + tlb_reset_dirty_range(&env->tlb_table[0][i], start1, length); + for(i = 0; i < CPU_TLB_SIZE; i++) + tlb_reset_dirty_range(&env->tlb_table[1][i], start1, length); + } + +#if !defined(CONFIG_SOFTMMU) + /* XXX: this is expensive */ + { + VirtPageDesc *p; + int j; + target_ulong addr; + + for(i = 0; i < L1_SIZE; i++) { + p = l1_virt_map[i]; + if (p) { + addr = i << (TARGET_PAGE_BITS + L2_BITS); + for(j = 0; j < L2_SIZE; j++) { + if (p->valid_tag == virt_valid_tag && + p->phys_addr >= start && p->phys_addr < end && + (p->prot & PROT_WRITE)) { + if (addr < MMAP_AREA_END) { + mprotect((void *)addr, TARGET_PAGE_SIZE, + p->prot & ~PROT_WRITE); + } + } + addr += TARGET_PAGE_SIZE; + p++; + } + } + } + } +#endif +} + +static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry) +{ + ram_addr_t ram_addr; + + if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM) { + ram_addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + + tlb_entry->addend - (unsigned long)phys_ram_base; + if (!cpu_physical_memory_is_dirty(ram_addr)) { + tlb_entry->addr_write |= IO_MEM_NOTDIRTY; + } + } +} + +/* update the TLB according to the current state of the dirty bits */ +void cpu_tlb_update_dirty(CPUState *env) +{ + int i; + for(i = 0; i < CPU_TLB_SIZE; i++) + tlb_update_dirty(&env->tlb_table[0][i]); + for(i = 0; i < CPU_TLB_SIZE; i++) + tlb_update_dirty(&env->tlb_table[1][i]); +} + +static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, + unsigned long start) +{ + unsigned long addr; + if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_NOTDIRTY) { + addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend; + if (addr == start) { + tlb_entry->addr_write = (tlb_entry->addr_write & TARGET_PAGE_MASK) | IO_MEM_RAM; + } + } +} + +/* update the TLB corresponding to virtual page vaddr and phys addr + addr so that it is no longer dirty */ +static inline void tlb_set_dirty(CPUState *env, + unsigned long addr, target_ulong vaddr) +{ + int i; + + addr &= TARGET_PAGE_MASK; + i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + tlb_set_dirty1(&env->tlb_table[0][i], addr); + tlb_set_dirty1(&env->tlb_table[1][i], addr); +} + +/* add a new TLB entry. At most one entry for a given virtual address + is permitted. Return 0 if OK or 2 if the page could not be mapped + (can only happen in non SOFTMMU mode for I/O pages or pages + conflicting with the host address space). */ +int tlb_set_page_exec(CPUState *env, target_ulong vaddr, + target_phys_addr_t paddr, int prot, + int is_user, int is_softmmu) +{ + PhysPageDesc *p; + unsigned long pd; + unsigned int index; + target_ulong address; + target_phys_addr_t addend; + int ret; + CPUTLBEntry *te; + + p = phys_page_find(paddr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } +#if defined(DEBUG_TLB) + printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x%08x prot=%x u=%d smmu=%d pd=0x%08lx\n", + vaddr, (int)paddr, prot, is_user, is_softmmu, pd); +#endif + + ret = 0; +#if !defined(CONFIG_SOFTMMU) + if (is_softmmu) +#endif + { + if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM) { + /* IO memory case */ + address = vaddr | pd; + addend = paddr; + } else { + /* standard memory */ + address = vaddr; + addend = (unsigned long)phys_ram_base + (pd & TARGET_PAGE_MASK); + } + + index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + addend -= vaddr; + te = &env->tlb_table[is_user][index]; + te->addend = addend; + if (prot & PAGE_READ) { + te->addr_read = address; + } else { + te->addr_read = -1; + } + if (prot & PAGE_EXEC) { + te->addr_code = address; + } else { + te->addr_code = -1; + } + if (prot & PAGE_WRITE) { + if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM) { + /* ROM: access is ignored (same as unassigned) */ + te->addr_write = vaddr | IO_MEM_ROM; + } else if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM && + !cpu_physical_memory_is_dirty(pd)) { + te->addr_write = vaddr | IO_MEM_NOTDIRTY; + } else { + te->addr_write = address; + } + } else { + te->addr_write = -1; + } + } +#if !defined(CONFIG_SOFTMMU) + else { + if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM) { + /* IO access: no mapping is done as it will be handled by the + soft MMU */ + if (!(env->hflags & HF_SOFTMMU_MASK)) + ret = 2; + } else { + void *map_addr; + + if (vaddr >= MMAP_AREA_END) { + ret = 2; + } else { + if (prot & PROT_WRITE) { + if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM || +#if defined(TARGET_HAS_SMC) || 1 + first_tb || +#endif + ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM && + !cpu_physical_memory_is_dirty(pd))) { + /* ROM: we do as if code was inside */ + /* if code is present, we only map as read only and save the + original mapping */ + VirtPageDesc *vp; + + vp = virt_page_find_alloc(vaddr >> TARGET_PAGE_BITS, 1); + vp->phys_addr = pd; + vp->prot = prot; + vp->valid_tag = virt_valid_tag; + prot &= ~PAGE_WRITE; + } + } + map_addr = mmap((void *)vaddr, TARGET_PAGE_SIZE, prot, + MAP_SHARED | MAP_FIXED, phys_ram_fd, (pd & TARGET_PAGE_MASK)); + if (map_addr == MAP_FAILED) { + cpu_abort(env, "mmap failed when mapped physical address 0x%08x to virtual address 0x%08x\n", + paddr, vaddr); + } + } + } + } +#endif + return ret; +} + +/* called from signal handler: invalidate the code and unprotect the + page. Return TRUE if the fault was succesfully handled. */ +int page_unprotect(target_ulong addr, unsigned long pc, void *puc) +{ +#if !defined(CONFIG_SOFTMMU) + VirtPageDesc *vp; + +#if defined(DEBUG_TLB) + printf("page_unprotect: addr=0x%08x\n", addr); +#endif + addr &= TARGET_PAGE_MASK; + + /* if it is not mapped, no need to worry here */ + if (addr >= MMAP_AREA_END) + return 0; + vp = virt_page_find(addr >> TARGET_PAGE_BITS); + if (!vp) + return 0; + /* NOTE: in this case, validate_tag is _not_ tested as it + validates only the code TLB */ + if (vp->valid_tag != virt_valid_tag) + return 0; + if (!(vp->prot & PAGE_WRITE)) + return 0; +#if defined(DEBUG_TLB) + printf("page_unprotect: addr=0x%08x phys_addr=0x%08x prot=%x\n", + addr, vp->phys_addr, vp->prot); +#endif + if (mprotect((void *)addr, TARGET_PAGE_SIZE, vp->prot) < 0) + cpu_abort(cpu_single_env, "error mprotect addr=0x%lx prot=%d\n", + (unsigned long)addr, vp->prot); + /* set the dirty bit */ + phys_ram_dirty[vp->phys_addr >> TARGET_PAGE_BITS] = 0xff; + /* flush the code inside */ + tb_invalidate_phys_page(vp->phys_addr, pc, puc); + return 1; +#else + return 0; +#endif +} + +#else + +void tlb_flush(CPUState *env, int flush_global) +{ +} + +void tlb_flush_page(CPUState *env, target_ulong addr) +{ +} + +int tlb_set_page_exec(CPUState *env, target_ulong vaddr, + target_phys_addr_t paddr, int prot, + int is_user, int is_softmmu) +{ + return 0; +} + +/* dump memory mappings */ +void page_dump(FILE *f) +{ + unsigned long start, end; + int i, j, prot, prot1; + PageDesc *p; + + fprintf(f, "%-8s %-8s %-8s %s\n", + "start", "end", "size", "prot"); + start = -1; + end = -1; + prot = 0; + for(i = 0; i <= L1_SIZE; i++) { + if (i < L1_SIZE) + p = l1_map[i]; + else + p = NULL; + for(j = 0;j < L2_SIZE; j++) { + if (!p) + prot1 = 0; + else + prot1 = p[j].flags; + if (prot1 != prot) { + end = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS); + if (start != -1) { + fprintf(f, "%08lx-%08lx %08lx %c%c%c\n", + start, end, end - start, + prot & PAGE_READ ? 'r' : '-', + prot & PAGE_WRITE ? 'w' : '-', + prot & PAGE_EXEC ? 'x' : '-'); + } + if (prot1 != 0) + start = end; + else + start = -1; + prot = prot1; + } + if (!p) + break; + } + } +} + +int page_get_flags(target_ulong address) +{ + PageDesc *p; + + p = page_find(address >> TARGET_PAGE_BITS); + if (!p) + return 0; + return p->flags; +} + +/* modify the flags of a page and invalidate the code if + necessary. The flag PAGE_WRITE_ORG is positionned automatically + depending on PAGE_WRITE */ +void page_set_flags(target_ulong start, target_ulong end, int flags) +{ + PageDesc *p; + target_ulong addr; + + start = start & TARGET_PAGE_MASK; + end = TARGET_PAGE_ALIGN(end); + if (flags & PAGE_WRITE) + flags |= PAGE_WRITE_ORG; + spin_lock(&tb_lock); + for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { + p = page_find_alloc(addr >> TARGET_PAGE_BITS); + /* if the write protection is set, then we invalidate the code + inside */ + if (!(p->flags & PAGE_WRITE) && + (flags & PAGE_WRITE) && + p->first_tb) { + tb_invalidate_phys_page(addr, 0, NULL); + } + p->flags = flags; + } + spin_unlock(&tb_lock); +} + +/* called from signal handler: invalidate the code and unprotect the + page. Return TRUE if the fault was succesfully handled. */ +int page_unprotect(target_ulong address, unsigned long pc, void *puc) +{ + unsigned int page_index, prot, pindex; + PageDesc *p, *p1; + target_ulong host_start, host_end, addr; + + host_start = address & qemu_host_page_mask; + page_index = host_start >> TARGET_PAGE_BITS; + p1 = page_find(page_index); + if (!p1) + return 0; + host_end = host_start + qemu_host_page_size; + p = p1; + prot = 0; + for(addr = host_start;addr < host_end; addr += TARGET_PAGE_SIZE) { + prot |= p->flags; + p++; + } + /* if the page was really writable, then we change its + protection back to writable */ + if (prot & PAGE_WRITE_ORG) { + pindex = (address - host_start) >> TARGET_PAGE_BITS; + if (!(p1[pindex].flags & PAGE_WRITE)) { + mprotect((void *)g2h(host_start), qemu_host_page_size, + (prot & PAGE_BITS) | PAGE_WRITE); + p1[pindex].flags |= PAGE_WRITE; + /* and since the content will be modified, we must invalidate + the corresponding translated code. */ + tb_invalidate_phys_page(address, pc, puc); +#ifdef DEBUG_TB_CHECK + tb_invalidate_check(address); +#endif + return 1; + } + } + return 0; +} + +/* call this function when system calls directly modify a memory area */ +/* ??? This should be redundant now we have lock_user. */ +void page_unprotect_range(target_ulong data, target_ulong data_size) +{ + target_ulong start, end, addr; + + start = data; + end = start + data_size; + start &= TARGET_PAGE_MASK; + end = TARGET_PAGE_ALIGN(end); + for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { + page_unprotect(addr, 0, NULL); + } +} + +static inline void tlb_set_dirty(CPUState *env, + unsigned long addr, target_ulong vaddr) +{ +} +#endif /* defined(CONFIG_USER_ONLY) */ + +/* register physical memory. 'size' must be a multiple of the target + page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an + io memory page */ +void cpu_register_physical_memory(target_phys_addr_t start_addr, + unsigned long size, + unsigned long phys_offset) +{ + target_phys_addr_t addr, end_addr; + PhysPageDesc *p; + + size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; + end_addr = start_addr + size; + for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) { + p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1); + p->phys_offset = phys_offset; + if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM) + phys_offset += TARGET_PAGE_SIZE; + } +} + +static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr) +{ + return 0; +} + +static void unassigned_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ +} + +static CPUReadMemoryFunc *unassigned_mem_read[3] = { + unassigned_mem_readb, + unassigned_mem_readb, + unassigned_mem_readb, +}; + +static CPUWriteMemoryFunc *unassigned_mem_write[3] = { + unassigned_mem_writeb, + unassigned_mem_writeb, + unassigned_mem_writeb, +}; + +static void notdirty_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + unsigned long ram_addr; + int dirty_flags; + ram_addr = addr - (unsigned long)phys_ram_base; + dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + if (!(dirty_flags & CODE_DIRTY_FLAG)) { +#if !defined(CONFIG_USER_ONLY) + tb_invalidate_phys_page_fast(ram_addr, 1); + dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; +#endif + } + stb_p((uint8_t *)(long)addr, val); +#ifdef USE_KQEMU + if (cpu_single_env->kqemu_enabled && + (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK) + kqemu_modify_page(cpu_single_env, ram_addr); +#endif + dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); + phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; + /* we remove the notdirty callback only if the code has been + flushed */ + if (dirty_flags == 0xff) + tlb_set_dirty(cpu_single_env, addr, cpu_single_env->mem_write_vaddr); +} + +static void notdirty_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + unsigned long ram_addr; + int dirty_flags; + ram_addr = addr - (unsigned long)phys_ram_base; + dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + if (!(dirty_flags & CODE_DIRTY_FLAG)) { +#if !defined(CONFIG_USER_ONLY) + tb_invalidate_phys_page_fast(ram_addr, 2); + dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; +#endif + } + stw_p((uint8_t *)(long)addr, val); +#ifdef USE_KQEMU + if (cpu_single_env->kqemu_enabled && + (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK) + kqemu_modify_page(cpu_single_env, ram_addr); +#endif + dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); + phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; + /* we remove the notdirty callback only if the code has been + flushed */ + if (dirty_flags == 0xff) + tlb_set_dirty(cpu_single_env, addr, cpu_single_env->mem_write_vaddr); +} + +static void notdirty_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + unsigned long ram_addr; + int dirty_flags; + ram_addr = addr - (unsigned long)phys_ram_base; + dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + if (!(dirty_flags & CODE_DIRTY_FLAG)) { +#if !defined(CONFIG_USER_ONLY) + tb_invalidate_phys_page_fast(ram_addr, 4); + dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; +#endif + } + stl_p((uint8_t *)(long)addr, val); +#ifdef USE_KQEMU + if (cpu_single_env->kqemu_enabled && + (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK) + kqemu_modify_page(cpu_single_env, ram_addr); +#endif + dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); + phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; + /* we remove the notdirty callback only if the code has been + flushed */ + if (dirty_flags == 0xff) + tlb_set_dirty(cpu_single_env, addr, cpu_single_env->mem_write_vaddr); +} + +static CPUReadMemoryFunc *error_mem_read[3] = { + NULL, /* never used */ + NULL, /* never used */ + NULL, /* never used */ +}; + +static CPUWriteMemoryFunc *notdirty_mem_write[3] = { + notdirty_mem_writeb, + notdirty_mem_writew, + notdirty_mem_writel, +}; + +static void io_mem_init(void) +{ + cpu_register_io_memory(IO_MEM_ROM >> IO_MEM_SHIFT, error_mem_read, unassigned_mem_write, NULL); + cpu_register_io_memory(IO_MEM_UNASSIGNED >> IO_MEM_SHIFT, unassigned_mem_read, unassigned_mem_write, NULL); + cpu_register_io_memory(IO_MEM_NOTDIRTY >> IO_MEM_SHIFT, error_mem_read, notdirty_mem_write, NULL); + io_mem_nb = 5; + + /* alloc dirty bits array */ + phys_ram_dirty = qemu_vmalloc(phys_ram_size >> TARGET_PAGE_BITS); + memset(phys_ram_dirty, 0xff, phys_ram_size >> TARGET_PAGE_BITS); +} + +/* mem_read and mem_write are arrays of functions containing the + function to access byte (index 0), word (index 1) and dword (index + 2). All functions must be supplied. If io_index is non zero, the + corresponding io zone is modified. If it is zero, a new io zone is + allocated. The return value can be used with + cpu_register_physical_memory(). (-1) is returned if error. */ +int cpu_register_io_memory(int io_index, + CPUReadMemoryFunc **mem_read, + CPUWriteMemoryFunc **mem_write, + void *opaque) +{ + int i; + + if (io_index <= 0) { + if (io_mem_nb >= IO_MEM_NB_ENTRIES) + return -1; + io_index = io_mem_nb++; + } else { + if (io_index >= IO_MEM_NB_ENTRIES) + return -1; + } + + for(i = 0;i < 3; i++) { + io_mem_read[io_index][i] = mem_read[i]; + io_mem_write[io_index][i] = mem_write[i]; + } + io_mem_opaque[io_index] = opaque; + return io_index << IO_MEM_SHIFT; +} + +CPUWriteMemoryFunc **cpu_get_io_memory_write(int io_index) +{ + return io_mem_write[io_index >> IO_MEM_SHIFT]; +} + +CPUReadMemoryFunc **cpu_get_io_memory_read(int io_index) +{ + return io_mem_read[io_index >> IO_MEM_SHIFT]; +} + +/* physical memory access (slow version, mainly for debug) */ +#if defined(CONFIG_USER_ONLY) +void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, + int len, int is_write) +{ + int l, flags; + target_ulong page; + void * p; + + while (len > 0) { + page = addr & TARGET_PAGE_MASK; + l = (page + TARGET_PAGE_SIZE) - addr; + if (l > len) + l = len; + flags = page_get_flags(page); + if (!(flags & PAGE_VALID)) + return; + if (is_write) { + if (!(flags & PAGE_WRITE)) + return; + p = lock_user(addr, len, 0); + memcpy(p, buf, len); + unlock_user(p, addr, len); + } else { + if (!(flags & PAGE_READ)) + return; + p = lock_user(addr, len, 1); + memcpy(buf, p, len); + unlock_user(p, addr, 0); + } + len -= l; + buf += l; + addr += l; + } +} + +#else +void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, + int len, int is_write) +{ + int l, io_index; + uint8_t *ptr; + uint32_t val; + target_phys_addr_t page; + unsigned long pd; + PhysPageDesc *p; + + while (len > 0) { + page = addr & TARGET_PAGE_MASK; + l = (page + TARGET_PAGE_SIZE) - addr; + if (l > len) + l = len; + p = phys_page_find(page >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if (is_write) { + if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { + io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + /* XXX: could force cpu_single_env to NULL to avoid + potential bugs */ + if (l >= 4 && ((addr & 3) == 0)) { + /* 32 bit write access */ + val = ldl_p(buf); + io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val); + l = 4; + } else if (l >= 2 && ((addr & 1) == 0)) { + /* 16 bit write access */ + val = lduw_p(buf); + io_mem_write[io_index][1](io_mem_opaque[io_index], addr, val); + l = 2; + } else { + /* 8 bit write access */ + val = ldub_p(buf); + io_mem_write[io_index][0](io_mem_opaque[io_index], addr, val); + l = 1; + } + } else { + unsigned long addr1; + addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); + /* RAM case */ + ptr = phys_ram_base + addr1; + memcpy(ptr, buf, l); + if (!cpu_physical_memory_is_dirty(addr1)) { + /* invalidate code */ + tb_invalidate_phys_page_range(addr1, addr1 + l, 0); + /* set dirty bit */ + phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= + (0xff & ~CODE_DIRTY_FLAG); + } + } + } else { + if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM) { + /* I/O case */ + io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + if (l >= 4 && ((addr & 3) == 0)) { + /* 32 bit read access */ + val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr); + stl_p(buf, val); + l = 4; + } else if (l >= 2 && ((addr & 1) == 0)) { + /* 16 bit read access */ + val = io_mem_read[io_index][1](io_mem_opaque[io_index], addr); + stw_p(buf, val); + l = 2; + } else { + /* 8 bit read access */ + val = io_mem_read[io_index][0](io_mem_opaque[io_index], addr); + stb_p(buf, val); + l = 1; + } + } else { + /* RAM case */ + ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + + (addr & ~TARGET_PAGE_MASK); + memcpy(buf, ptr, l); + } + } + len -= l; + buf += l; + addr += l; + } +} + +/* used for ROM loading : can write in RAM and ROM */ +void cpu_physical_memory_write_rom(target_phys_addr_t addr, + const uint8_t *buf, int len) +{ + int l; + uint8_t *ptr; + target_phys_addr_t page; + unsigned long pd; + PhysPageDesc *p; + + while (len > 0) { + page = addr & TARGET_PAGE_MASK; + l = (page + TARGET_PAGE_SIZE) - addr; + if (l > len) + l = len; + p = phys_page_find(page >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM && + (pd & ~TARGET_PAGE_MASK) != IO_MEM_ROM) { + /* do nothing */ + } else { + unsigned long addr1; + addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); + /* ROM/RAM case */ + ptr = phys_ram_base + addr1; + memcpy(ptr, buf, l); + } + len -= l; + buf += l; + addr += l; + } +} + + +/* warning: addr must be aligned */ +uint32_t ldl_phys(target_phys_addr_t addr) +{ + int io_index; + uint8_t *ptr; + uint32_t val; + unsigned long pd; + PhysPageDesc *p; + + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM) { + /* I/O case */ + io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr); + } else { + /* RAM case */ + ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + + (addr & ~TARGET_PAGE_MASK); + val = ldl_p(ptr); + } + return val; +} + +/* warning: addr must be aligned */ +uint64_t ldq_phys(target_phys_addr_t addr) +{ + int io_index; + uint8_t *ptr; + uint64_t val; + unsigned long pd; + PhysPageDesc *p; + + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM) { + /* I/O case */ + io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); +#ifdef TARGET_WORDS_BIGENDIAN + val = (uint64_t)io_mem_read[io_index][2](io_mem_opaque[io_index], addr) << 32; + val |= io_mem_read[io_index][2](io_mem_opaque[io_index], addr + 4); +#else + val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr); + val |= (uint64_t)io_mem_read[io_index][2](io_mem_opaque[io_index], addr + 4) << 32; +#endif + } else { + /* RAM case */ + ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + + (addr & ~TARGET_PAGE_MASK); + val = ldq_p(ptr); + } + return val; +} + +/* XXX: optimize */ +uint32_t ldub_phys(target_phys_addr_t addr) +{ + uint8_t val; + cpu_physical_memory_read(addr, &val, 1); + return val; +} + +/* XXX: optimize */ +uint32_t lduw_phys(target_phys_addr_t addr) +{ + uint16_t val; + cpu_physical_memory_read(addr, (uint8_t *)&val, 2); + return tswap16(val); +} + +/* warning: addr must be aligned. The ram page is not masked as dirty + and the code inside is not invalidated. It is useful if the dirty + bits are used to track modified PTEs */ +void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val) +{ + int io_index; + uint8_t *ptr; + unsigned long pd; + PhysPageDesc *p; + + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { + io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val); + } else { + ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + + (addr & ~TARGET_PAGE_MASK); + stl_p(ptr, val); + } +} + +/* warning: addr must be aligned */ +void stl_phys(target_phys_addr_t addr, uint32_t val) +{ + int io_index; + uint8_t *ptr; + unsigned long pd; + PhysPageDesc *p; + + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { + io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val); + } else { + unsigned long addr1; + addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); + /* RAM case */ + ptr = phys_ram_base + addr1; + stl_p(ptr, val); + if (!cpu_physical_memory_is_dirty(addr1)) { + /* invalidate code */ + tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); + /* set dirty bit */ + phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= + (0xff & ~CODE_DIRTY_FLAG); + } + } +} + +/* XXX: optimize */ +void stb_phys(target_phys_addr_t addr, uint32_t val) +{ + uint8_t v = val; + cpu_physical_memory_write(addr, &v, 1); +} + +/* XXX: optimize */ +void stw_phys(target_phys_addr_t addr, uint32_t val) +{ + uint16_t v = tswap16(val); + cpu_physical_memory_write(addr, (const uint8_t *)&v, 2); +} + +/* XXX: optimize */ +void stq_phys(target_phys_addr_t addr, uint64_t val) +{ + val = tswap64(val); + cpu_physical_memory_write(addr, (const uint8_t *)&val, 8); +} + +#endif + +/* virtual memory access for debug */ +int cpu_memory_rw_debug(CPUState *env, target_ulong addr, + uint8_t *buf, int len, int is_write) +{ + int l; + target_ulong page, phys_addr; + + while (len > 0) { + page = addr & TARGET_PAGE_MASK; + phys_addr = cpu_get_phys_page_debug(env, page); + /* if no physical page mapped, return an error */ + if (phys_addr == -1) + return -1; + l = (page + TARGET_PAGE_SIZE) - addr; + if (l > len) + l = len; + cpu_physical_memory_rw(phys_addr + (addr & ~TARGET_PAGE_MASK), + buf, l, is_write); + len -= l; + buf += l; + addr += l; + } + return 0; +} + +void dump_exec_info(FILE *f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +{ + int i, target_code_size, max_target_code_size; + int direct_jmp_count, direct_jmp2_count, cross_page; + TranslationBlock *tb; + + target_code_size = 0; + max_target_code_size = 0; + cross_page = 0; + direct_jmp_count = 0; + direct_jmp2_count = 0; + for(i = 0; i < nb_tbs; i++) { + tb = &tbs[i]; + target_code_size += tb->size; + if (tb->size > max_target_code_size) + max_target_code_size = tb->size; + if (tb->page_addr[1] != -1) + cross_page++; + if (tb->tb_next_offset[0] != 0xffff) { + direct_jmp_count++; + if (tb->tb_next_offset[1] != 0xffff) { + direct_jmp2_count++; + } + } + } + /* XXX: avoid using doubles ? */ + cpu_fprintf(f, "TB count %d\n", nb_tbs); + cpu_fprintf(f, "TB avg target size %d max=%d bytes\n", + nb_tbs ? target_code_size / nb_tbs : 0, + max_target_code_size); + cpu_fprintf(f, "TB avg host size %d bytes (expansion ratio: %0.1f)\n", + nb_tbs ? (code_gen_ptr - code_gen_buffer) / nb_tbs : 0, + target_code_size ? (double) (code_gen_ptr - code_gen_buffer) / target_code_size : 0); + cpu_fprintf(f, "cross page TB count %d (%d%%)\n", + cross_page, + nb_tbs ? (cross_page * 100) / nb_tbs : 0); + cpu_fprintf(f, "direct jump count %d (%d%%) (2 jumps=%d %d%%)\n", + direct_jmp_count, + nb_tbs ? (direct_jmp_count * 100) / nb_tbs : 0, + direct_jmp2_count, + nb_tbs ? (direct_jmp2_count * 100) / nb_tbs : 0); + cpu_fprintf(f, "TB flush count %d\n", tb_flush_count); + cpu_fprintf(f, "TB invalidate count %d\n", tb_phys_invalidate_count); + cpu_fprintf(f, "TLB flush count %d\n", tlb_flush_count); +} + +#if !defined(CONFIG_USER_ONLY) + +#define MMUSUFFIX _cmmu +#define GETPC() NULL +#define env cpu_single_env +#define SOFTMMU_CODE_ACCESS + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" + +#undef env + +#endif diff --git a/tools/ioemu/fpu/CVS/Entries b/tools/ioemu/fpu/CVS/Entries new file mode 100644 index 0000000000..4da09e72d9 --- /dev/null +++ b/tools/ioemu/fpu/CVS/Entries @@ -0,0 +1,7 @@ +/softfloat-macros.h/1.1/Sun Mar 13 16:54:06 2005//Trelease_0_8_1 +/softfloat-native.c/1.3/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/softfloat-native.h/1.6/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/softfloat-specialize.h/1.1/Sun Mar 13 16:54:06 2005//Trelease_0_8_1 +/softfloat.c/1.2/Sun Mar 13 18:52:28 2005//Trelease_0_8_1 +/softfloat.h/1.3/Thu May 25 18:22:32 2006//Trelease_0_8_1 +D diff --git a/tools/ioemu/fpu/CVS/Repository b/tools/ioemu/fpu/CVS/Repository new file mode 100644 index 0000000000..6aaee19f2c --- /dev/null +++ b/tools/ioemu/fpu/CVS/Repository @@ -0,0 +1 @@ +qemu/fpu diff --git a/tools/ioemu/fpu/CVS/Root b/tools/ioemu/fpu/CVS/Root new file mode 100644 index 0000000000..e8a9815e6a --- /dev/null +++ b/tools/ioemu/fpu/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.savannah.nongnu.org:/sources/qemu diff --git a/tools/ioemu/fpu/CVS/Tag b/tools/ioemu/fpu/CVS/Tag new file mode 100644 index 0000000000..31c251a4bf --- /dev/null +++ b/tools/ioemu/fpu/CVS/Tag @@ -0,0 +1 @@ +Nrelease_0_8_1 diff --git a/tools/ioemu/fpu/softfloat-macros.h b/tools/ioemu/fpu/softfloat-macros.h new file mode 100644 index 0000000000..2c8f18b1ce --- /dev/null +++ b/tools/ioemu/fpu/softfloat-macros.h @@ -0,0 +1,720 @@ + +/*============================================================================ + +This C source fragment is part of the SoftFloat IEC/IEEE Floating-point +Arithmetic Package, Release 2b. + +Written by John R. Hauser. This work was made possible in part by the +International Computer Science Institute, located at Suite 600, 1947 Center +Street, Berkeley, California 94704. Funding was partially provided by the +National Science Foundation under grant MIP-9311980. The original version +of this code was written as part of a project to build a fixed-point vector +processor in collaboration with the University of California at Berkeley, +overseen by Profs. Nelson Morgan and John Wawrzynek. More information +is available through the Web page `http://www.cs.berkeley.edu/~jhauser/ +arithmetic/SoftFloat.html'. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has +been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES +RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS +AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES, +COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE +EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE +INSTITUTE (possibly via similar legal notice) AGAINST ALL LOSSES, COSTS, OR +OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE. + +Derivative works are acceptable, even for commercial purposes, so long as +(1) the source code for the derivative work includes prominent notice that +the work is derivative, and (2) the source code includes prominent notice with +these four paragraphs for those parts of this code that are retained. + +=============================================================================*/ + +/*---------------------------------------------------------------------------- +| Shifts `a' right by the number of bits given in `count'. If any nonzero +| bits are shifted off, they are ``jammed'' into the least significant bit of +| the result by setting the least significant bit to 1. The value of `count' +| can be arbitrarily large; in particular, if `count' is greater than 32, the +| result will be either 0 or 1, depending on whether `a' is zero or nonzero. +| The result is stored in the location pointed to by `zPtr'. +*----------------------------------------------------------------------------*/ + +INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr ) +{ + bits32 z; + + if ( count == 0 ) { + z = a; + } + else if ( count < 32 ) { + z = ( a>>count ) | ( ( a<<( ( - count ) & 31 ) ) != 0 ); + } + else { + z = ( a != 0 ); + } + *zPtr = z; + +} + +/*---------------------------------------------------------------------------- +| Shifts `a' right by the number of bits given in `count'. If any nonzero +| bits are shifted off, they are ``jammed'' into the least significant bit of +| the result by setting the least significant bit to 1. The value of `count' +| can be arbitrarily large; in particular, if `count' is greater than 64, the +| result will be either 0 or 1, depending on whether `a' is zero or nonzero. +| The result is stored in the location pointed to by `zPtr'. +*----------------------------------------------------------------------------*/ + +INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr ) +{ + bits64 z; + + if ( count == 0 ) { + z = a; + } + else if ( count < 64 ) { + z = ( a>>count ) | ( ( a<<( ( - count ) & 63 ) ) != 0 ); + } + else { + z = ( a != 0 ); + } + *zPtr = z; + +} + +/*---------------------------------------------------------------------------- +| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by 64 +| _plus_ the number of bits given in `count'. The shifted result is at most +| 64 nonzero bits; this is stored at the location pointed to by `z0Ptr'. The +| bits shifted off form a second 64-bit result as follows: The _last_ bit +| shifted off is the most-significant bit of the extra result, and the other +| 63 bits of the extra result are all zero if and only if _all_but_the_last_ +| bits shifted off were all zero. This extra result is stored in the location +| pointed to by `z1Ptr'. The value of `count' can be arbitrarily large. +| (This routine makes more sense if `a0' and `a1' are considered to form +| a fixed-point value with binary point between `a0' and `a1'. This fixed- +| point value is shifted right by the number of bits given in `count', and +| the integer part of the result is returned at the location pointed to by +| `z0Ptr'. The fractional part of the result may be slightly corrupted as +| described above, and is returned at the location pointed to by `z1Ptr'.) +*----------------------------------------------------------------------------*/ + +INLINE void + shift64ExtraRightJamming( + bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + bits64 z0, z1; + int8 negCount = ( - count ) & 63; + + if ( count == 0 ) { + z1 = a1; + z0 = a0; + } + else if ( count < 64 ) { + z1 = ( a0<>count; + } + else { + if ( count == 64 ) { + z1 = a0 | ( a1 != 0 ); + } + else { + z1 = ( ( a0 | a1 ) != 0 ); + } + z0 = 0; + } + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/*---------------------------------------------------------------------------- +| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the +| number of bits given in `count'. Any bits shifted off are lost. The value +| of `count' can be arbitrarily large; in particular, if `count' is greater +| than 128, the result will be 0. The result is broken into two 64-bit pieces +| which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. +*----------------------------------------------------------------------------*/ + +INLINE void + shift128Right( + bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + bits64 z0, z1; + int8 negCount = ( - count ) & 63; + + if ( count == 0 ) { + z1 = a1; + z0 = a0; + } + else if ( count < 64 ) { + z1 = ( a0<>count ); + z0 = a0>>count; + } + else { + z1 = ( count < 64 ) ? ( a0>>( count & 63 ) ) : 0; + z0 = 0; + } + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/*---------------------------------------------------------------------------- +| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the +| number of bits given in `count'. If any nonzero bits are shifted off, they +| are ``jammed'' into the least significant bit of the result by setting the +| least significant bit to 1. The value of `count' can be arbitrarily large; +| in particular, if `count' is greater than 128, the result will be either +| 0 or 1, depending on whether the concatenation of `a0' and `a1' is zero or +| nonzero. The result is broken into two 64-bit pieces which are stored at +| the locations pointed to by `z0Ptr' and `z1Ptr'. +*----------------------------------------------------------------------------*/ + +INLINE void + shift128RightJamming( + bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + bits64 z0, z1; + int8 negCount = ( - count ) & 63; + + if ( count == 0 ) { + z1 = a1; + z0 = a0; + } + else if ( count < 64 ) { + z1 = ( a0<>count ) | ( ( a1<>count; + } + else { + if ( count == 64 ) { + z1 = a0 | ( a1 != 0 ); + } + else if ( count < 128 ) { + z1 = ( a0>>( count & 63 ) ) | ( ( ( a0<>count ); + z0 = a0>>count; + } + else { + if ( count == 64 ) { + z2 = a1; + z1 = a0; + } + else { + a2 |= a1; + if ( count < 128 ) { + z2 = a0<>( count & 63 ); + } + else { + z2 = ( count == 128 ) ? a0 : ( a0 != 0 ); + z1 = 0; + } + } + z0 = 0; + } + z2 |= ( a2 != 0 ); + } + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/*---------------------------------------------------------------------------- +| Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the +| number of bits given in `count'. Any bits shifted off are lost. The value +| of `count' must be less than 64. The result is broken into two 64-bit +| pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. +*----------------------------------------------------------------------------*/ + +INLINE void + shortShift128Left( + bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + + *z1Ptr = a1<>( ( - count ) & 63 ) ); + +} + +/*---------------------------------------------------------------------------- +| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' left +| by the number of bits given in `count'. Any bits shifted off are lost. +| The value of `count' must be less than 64. The result is broken into three +| 64-bit pieces which are stored at the locations pointed to by `z0Ptr', +| `z1Ptr', and `z2Ptr'. +*----------------------------------------------------------------------------*/ + +INLINE void + shortShift192Left( + bits64 a0, + bits64 a1, + bits64 a2, + int16 count, + bits64 *z0Ptr, + bits64 *z1Ptr, + bits64 *z2Ptr + ) +{ + bits64 z0, z1, z2; + int8 negCount; + + z2 = a2<>negCount; + z0 |= a1>>negCount; + } + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/*---------------------------------------------------------------------------- +| Adds the 128-bit value formed by concatenating `a0' and `a1' to the 128-bit +| value formed by concatenating `b0' and `b1'. Addition is modulo 2^128, so +| any carry out is lost. The result is broken into two 64-bit pieces which +| are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. +*----------------------------------------------------------------------------*/ + +INLINE void + add128( + bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + bits64 z1; + + z1 = a1 + b1; + *z1Ptr = z1; + *z0Ptr = a0 + b0 + ( z1 < a1 ); + +} + +/*---------------------------------------------------------------------------- +| Adds the 192-bit value formed by concatenating `a0', `a1', and `a2' to the +| 192-bit value formed by concatenating `b0', `b1', and `b2'. Addition is +| modulo 2^192, so any carry out is lost. The result is broken into three +| 64-bit pieces which are stored at the locations pointed to by `z0Ptr', +| `z1Ptr', and `z2Ptr'. +*----------------------------------------------------------------------------*/ + +INLINE void + add192( + bits64 a0, + bits64 a1, + bits64 a2, + bits64 b0, + bits64 b1, + bits64 b2, + bits64 *z0Ptr, + bits64 *z1Ptr, + bits64 *z2Ptr + ) +{ + bits64 z0, z1, z2; + int8 carry0, carry1; + + z2 = a2 + b2; + carry1 = ( z2 < a2 ); + z1 = a1 + b1; + carry0 = ( z1 < a1 ); + z0 = a0 + b0; + z1 += carry1; + z0 += ( z1 < carry1 ); + z0 += carry0; + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/*---------------------------------------------------------------------------- +| Subtracts the 128-bit value formed by concatenating `b0' and `b1' from the +| 128-bit value formed by concatenating `a0' and `a1'. Subtraction is modulo +| 2^128, so any borrow out (carry out) is lost. The result is broken into two +| 64-bit pieces which are stored at the locations pointed to by `z0Ptr' and +| `z1Ptr'. +*----------------------------------------------------------------------------*/ + +INLINE void + sub128( + bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + + *z1Ptr = a1 - b1; + *z0Ptr = a0 - b0 - ( a1 < b1 ); + +} + +/*---------------------------------------------------------------------------- +| Subtracts the 192-bit value formed by concatenating `b0', `b1', and `b2' +| from the 192-bit value formed by concatenating `a0', `a1', and `a2'. +| Subtraction is modulo 2^192, so any borrow out (carry out) is lost. The +| result is broken into three 64-bit pieces which are stored at the locations +| pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'. +*----------------------------------------------------------------------------*/ + +INLINE void + sub192( + bits64 a0, + bits64 a1, + bits64 a2, + bits64 b0, + bits64 b1, + bits64 b2, + bits64 *z0Ptr, + bits64 *z1Ptr, + bits64 *z2Ptr + ) +{ + bits64 z0, z1, z2; + int8 borrow0, borrow1; + + z2 = a2 - b2; + borrow1 = ( a2 < b2 ); + z1 = a1 - b1; + borrow0 = ( a1 < b1 ); + z0 = a0 - b0; + z0 -= ( z1 < borrow1 ); + z1 -= borrow1; + z0 -= borrow0; + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/*---------------------------------------------------------------------------- +| Multiplies `a' by `b' to obtain a 128-bit product. The product is broken +| into two 64-bit pieces which are stored at the locations pointed to by +| `z0Ptr' and `z1Ptr'. +*----------------------------------------------------------------------------*/ + +INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + bits32 aHigh, aLow, bHigh, bLow; + bits64 z0, zMiddleA, zMiddleB, z1; + + aLow = a; + aHigh = a>>32; + bLow = b; + bHigh = b>>32; + z1 = ( (bits64) aLow ) * bLow; + zMiddleA = ( (bits64) aLow ) * bHigh; + zMiddleB = ( (bits64) aHigh ) * bLow; + z0 = ( (bits64) aHigh ) * bHigh; + zMiddleA += zMiddleB; + z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 ); + zMiddleA <<= 32; + z1 += zMiddleA; + z0 += ( z1 < zMiddleA ); + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/*---------------------------------------------------------------------------- +| Multiplies the 128-bit value formed by concatenating `a0' and `a1' by +| `b' to obtain a 192-bit product. The product is broken into three 64-bit +| pieces which are stored at the locations pointed to by `z0Ptr', `z1Ptr', and +| `z2Ptr'. +*----------------------------------------------------------------------------*/ + +INLINE void + mul128By64To192( + bits64 a0, + bits64 a1, + bits64 b, + bits64 *z0Ptr, + bits64 *z1Ptr, + bits64 *z2Ptr + ) +{ + bits64 z0, z1, z2, more1; + + mul64To128( a1, b, &z1, &z2 ); + mul64To128( a0, b, &z0, &more1 ); + add128( z0, more1, 0, z1, &z0, &z1 ); + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/*---------------------------------------------------------------------------- +| Multiplies the 128-bit value formed by concatenating `a0' and `a1' to the +| 128-bit value formed by concatenating `b0' and `b1' to obtain a 256-bit +| product. The product is broken into four 64-bit pieces which are stored at +| the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'. +*----------------------------------------------------------------------------*/ + +INLINE void + mul128To256( + bits64 a0, + bits64 a1, + bits64 b0, + bits64 b1, + bits64 *z0Ptr, + bits64 *z1Ptr, + bits64 *z2Ptr, + bits64 *z3Ptr + ) +{ + bits64 z0, z1, z2, z3; + bits64 more1, more2; + + mul64To128( a1, b1, &z2, &z3 ); + mul64To128( a1, b0, &z1, &more2 ); + add128( z1, more2, 0, z2, &z1, &z2 ); + mul64To128( a0, b0, &z0, &more1 ); + add128( z0, more1, 0, z1, &z0, &z1 ); + mul64To128( a0, b1, &more1, &more2 ); + add128( more1, more2, 0, z2, &more1, &z2 ); + add128( z0, z1, 0, more1, &z0, &z1 ); + *z3Ptr = z3; + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/*---------------------------------------------------------------------------- +| Returns an approximation to the 64-bit integer quotient obtained by dividing +| `b' into the 128-bit value formed by concatenating `a0' and `a1'. The +| divisor `b' must be at least 2^63. If q is the exact quotient truncated +| toward zero, the approximation returned lies between q and q + 2 inclusive. +| If the exact quotient q is larger than 64 bits, the maximum positive 64-bit +| unsigned integer is returned. +*----------------------------------------------------------------------------*/ + +static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b ) +{ + bits64 b0, b1; + bits64 rem0, rem1, term0, term1; + bits64 z; + + if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF ); + b0 = b>>32; + z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32; + mul64To128( b, z, &term0, &term1 ); + sub128( a0, a1, term0, term1, &rem0, &rem1 ); + while ( ( (sbits64) rem0 ) < 0 ) { + z -= LIT64( 0x100000000 ); + b1 = b<<32; + add128( rem0, rem1, b0, b1, &rem0, &rem1 ); + } + rem0 = ( rem0<<32 ) | ( rem1>>32 ); + z |= ( b0<<32 <= rem0 ) ? 0xFFFFFFFF : rem0 / b0; + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns an approximation to the square root of the 32-bit significand given +| by `a'. Considered as an integer, `a' must be at least 2^31. If bit 0 of +| `aExp' (the least significant bit) is 1, the integer returned approximates +| 2^31*sqrt(`a'/2^31), where `a' is considered an integer. If bit 0 of `aExp' +| is 0, the integer returned approximates 2^31*sqrt(`a'/2^30). In either +| case, the approximation returned lies strictly within +/-2 of the exact +| value. +*----------------------------------------------------------------------------*/ + +static bits32 estimateSqrt32( int16 aExp, bits32 a ) +{ + static const bits16 sqrtOddAdjustments[] = { + 0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0, + 0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67 + }; + static const bits16 sqrtEvenAdjustments[] = { + 0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E, + 0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002 + }; + int8 index; + bits32 z; + + index = ( a>>27 ) & 15; + if ( aExp & 1 ) { + z = 0x4000 + ( a>>17 ) - sqrtOddAdjustments[ index ]; + z = ( ( a / z )<<14 ) + ( z<<15 ); + a >>= 1; + } + else { + z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ index ]; + z = a / z + z; + z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 ); + if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 ); + } + return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 ); + +} + +/*---------------------------------------------------------------------------- +| Returns the number of leading 0 bits before the most-significant 1 bit of +| `a'. If `a' is zero, 32 is returned. +*----------------------------------------------------------------------------*/ + +static int8 countLeadingZeros32( bits32 a ) +{ + static const int8 countLeadingZerosHigh[] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + int8 shiftCount; + + shiftCount = 0; + if ( a < 0x10000 ) { + shiftCount += 16; + a <<= 16; + } + if ( a < 0x1000000 ) { + shiftCount += 8; + a <<= 8; + } + shiftCount += countLeadingZerosHigh[ a>>24 ]; + return shiftCount; + +} + +/*---------------------------------------------------------------------------- +| Returns the number of leading 0 bits before the most-significant 1 bit of +| `a'. If `a' is zero, 64 is returned. +*----------------------------------------------------------------------------*/ + +static int8 countLeadingZeros64( bits64 a ) +{ + int8 shiftCount; + + shiftCount = 0; + if ( a < ( (bits64) 1 )<<32 ) { + shiftCount += 32; + } + else { + a >>= 32; + } + shiftCount += countLeadingZeros32( a ); + return shiftCount; + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' +| is equal to the 128-bit value formed by concatenating `b0' and `b1'. +| Otherwise, returns 0. +*----------------------------------------------------------------------------*/ + +INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +{ + + return ( a0 == b0 ) && ( a1 == b1 ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less +| than or equal to the 128-bit value formed by concatenating `b0' and `b1'. +| Otherwise, returns 0. +*----------------------------------------------------------------------------*/ + +INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +{ + + return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less +| than the 128-bit value formed by concatenating `b0' and `b1'. Otherwise, +| returns 0. +*----------------------------------------------------------------------------*/ + +INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +{ + + return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is +| not equal to the 128-bit value formed by concatenating `b0' and `b1'. +| Otherwise, returns 0. +*----------------------------------------------------------------------------*/ + +INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +{ + + return ( a0 != b0 ) || ( a1 != b1 ); + +} + diff --git a/tools/ioemu/fpu/softfloat-native.c b/tools/ioemu/fpu/softfloat-native.c new file mode 100644 index 0000000000..e54820239c --- /dev/null +++ b/tools/ioemu/fpu/softfloat-native.c @@ -0,0 +1,363 @@ +/* Native implementation of soft float functions. Only a single status + context is supported */ +#include "softfloat.h" +#include + +void set_float_rounding_mode(int val STATUS_PARAM) +{ + STATUS(float_rounding_mode) = val; +#if defined(_BSD) && !defined(__APPLE__) + fpsetround(val); +#elif defined(__arm__) + /* nothing to do */ +#else + fesetround(val); +#endif +} + +#ifdef FLOATX80 +void set_floatx80_rounding_precision(int val STATUS_PARAM) +{ + STATUS(floatx80_rounding_precision) = val; +} +#endif + +#if defined(_BSD) +#define lrint(d) ((long)rint(d)) +#define llrint(d) ((long long)rint(d)) +#endif + +#if defined(__powerpc__) + +/* correct (but slow) PowerPC rint() (glibc version is incorrect) */ +double qemu_rint(double x) +{ + double y = 4503599627370496.0; + if (fabs(x) >= y) + return x; + if (x < 0) + y = -y; + y = (x + y) - y; + if (y == 0.0) + y = copysign(y, x); + return y; +} + +#define rint qemu_rint +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE integer-to-floating-point conversion routines. +*----------------------------------------------------------------------------*/ +float32 int32_to_float32(int v STATUS_PARAM) +{ + return (float32)v; +} + +float64 int32_to_float64(int v STATUS_PARAM) +{ + return (float64)v; +} + +#ifdef FLOATX80 +floatx80 int32_to_floatx80(int v STATUS_PARAM) +{ + return (floatx80)v; +} +#endif +float32 int64_to_float32( int64_t v STATUS_PARAM) +{ + return (float32)v; +} +float64 int64_to_float64( int64_t v STATUS_PARAM) +{ + return (float64)v; +} +#ifdef FLOATX80 +floatx80 int64_to_floatx80( int64_t v STATUS_PARAM) +{ + return (floatx80)v; +} +#endif + +/* XXX: this code implements the x86 behaviour, not the IEEE one. */ +#if HOST_LONG_BITS == 32 +static inline int long_to_int32(long a) +{ + return a; +} +#else +static inline int long_to_int32(long a) +{ + if (a != (int32_t)a) + a = 0x80000000; + return a; +} +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE single-precision conversion routines. +*----------------------------------------------------------------------------*/ +int float32_to_int32( float32 a STATUS_PARAM) +{ + return long_to_int32(lrintf(a)); +} +int float32_to_int32_round_to_zero( float32 a STATUS_PARAM) +{ + return (int)a; +} +int64_t float32_to_int64( float32 a STATUS_PARAM) +{ + return llrintf(a); +} + +int64_t float32_to_int64_round_to_zero( float32 a STATUS_PARAM) +{ + return (int64_t)a; +} + +float64 float32_to_float64( float32 a STATUS_PARAM) +{ + return a; +} +#ifdef FLOATX80 +floatx80 float32_to_floatx80( float32 a STATUS_PARAM) +{ + return a; +} +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE single-precision operations. +*----------------------------------------------------------------------------*/ +float32 float32_round_to_int( float32 a STATUS_PARAM) +{ + return rintf(a); +} + +float32 float32_rem( float32 a, float32 b STATUS_PARAM) +{ + return remainderf(a, b); +} + +float32 float32_sqrt( float32 a STATUS_PARAM) +{ + return sqrtf(a); +} +char float32_compare( float32 a, float32 b STATUS_PARAM ) +{ + if (a < b) { + return -1; + } else if (a == b) { + return 0; + } else if (a > b) { + return 1; + } else { + return 2; + } +} +char float32_compare_quiet( float32 a, float32 b STATUS_PARAM ) +{ + if (isless(a, b)) { + return -1; + } else if (a == b) { + return 0; + } else if (isgreater(a, b)) { + return 1; + } else { + return 2; + } +} +char float32_is_signaling_nan( float32 a1) +{ + float32u u; + uint32_t a; + u.f = a1; + a = u.i; + return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF ); +} + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE double-precision conversion routines. +*----------------------------------------------------------------------------*/ +int float64_to_int32( float64 a STATUS_PARAM) +{ + return long_to_int32(lrint(a)); +} +int float64_to_int32_round_to_zero( float64 a STATUS_PARAM) +{ + return (int)a; +} +int64_t float64_to_int64( float64 a STATUS_PARAM) +{ + return llrint(a); +} +int64_t float64_to_int64_round_to_zero( float64 a STATUS_PARAM) +{ + return (int64_t)a; +} +float32 float64_to_float32( float64 a STATUS_PARAM) +{ + return a; +} +#ifdef FLOATX80 +floatx80 float64_to_floatx80( float64 a STATUS_PARAM) +{ + return a; +} +#endif +#ifdef FLOAT128 +float128 float64_to_float128( float64 a STATUS_PARAM) +{ + return a; +} +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE double-precision operations. +*----------------------------------------------------------------------------*/ +float64 float64_round_to_int( float64 a STATUS_PARAM ) +{ +#if defined(__arm__) + switch(STATUS(float_rounding_mode)) { + default: + case float_round_nearest_even: + asm("rndd %0, %1" : "=f" (a) : "f"(a)); + break; + case float_round_down: + asm("rnddm %0, %1" : "=f" (a) : "f"(a)); + break; + case float_round_up: + asm("rnddp %0, %1" : "=f" (a) : "f"(a)); + break; + case float_round_to_zero: + asm("rnddz %0, %1" : "=f" (a) : "f"(a)); + break; + } +#else + return rint(a); +#endif +} + +float64 float64_rem( float64 a, float64 b STATUS_PARAM) +{ + return remainder(a, b); +} + +float64 float64_sqrt( float64 a STATUS_PARAM) +{ + return sqrt(a); +} +char float64_compare( float64 a, float64 b STATUS_PARAM ) +{ + if (a < b) { + return -1; + } else if (a == b) { + return 0; + } else if (a > b) { + return 1; + } else { + return 2; + } +} +char float64_compare_quiet( float64 a, float64 b STATUS_PARAM ) +{ + if (isless(a, b)) { + return -1; + } else if (a == b) { + return 0; + } else if (isgreater(a, b)) { + return 1; + } else { + return 2; + } +} +char float64_is_signaling_nan( float64 a1) +{ + float64u u; + uint64_t a; + u.f = a1; + a = u.i; + return + ( ( ( a>>51 ) & 0xFFF ) == 0xFFE ) + && ( a & LIT64( 0x0007FFFFFFFFFFFF ) ); + +} + +#ifdef FLOATX80 + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE extended double-precision conversion routines. +*----------------------------------------------------------------------------*/ +int floatx80_to_int32( floatx80 a STATUS_PARAM) +{ + return long_to_int32(lrintl(a)); +} +int floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM) +{ + return (int)a; +} +int64_t floatx80_to_int64( floatx80 a STATUS_PARAM) +{ + return llrintl(a); +} +int64_t floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM) +{ + return (int64_t)a; +} +float32 floatx80_to_float32( floatx80 a STATUS_PARAM) +{ + return a; +} +float64 floatx80_to_float64( floatx80 a STATUS_PARAM) +{ + return a; +} + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE extended double-precision operations. +*----------------------------------------------------------------------------*/ +floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM) +{ + return rintl(a); +} +floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM) +{ + return remainderl(a, b); +} +floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM) +{ + return sqrtl(a); +} +char floatx80_compare( floatx80 a, floatx80 b STATUS_PARAM ) +{ + if (a < b) { + return -1; + } else if (a == b) { + return 0; + } else if (a > b) { + return 1; + } else { + return 2; + } +} +char floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM ) +{ + if (isless(a, b)) { + return -1; + } else if (a == b) { + return 0; + } else if (isgreater(a, b)) { + return 1; + } else { + return 2; + } +} +char floatx80_is_signaling_nan( floatx80 a1) +{ + floatx80u u; + u.f = a1; + return ( ( u.i.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( u.i.low<<1 ); +} + +#endif diff --git a/tools/ioemu/fpu/softfloat-native.h b/tools/ioemu/fpu/softfloat-native.h new file mode 100644 index 0000000000..e7c08b89f4 --- /dev/null +++ b/tools/ioemu/fpu/softfloat-native.h @@ -0,0 +1,359 @@ +/* Native implementation of soft float functions */ +#include + +#if (defined(_BSD) && !defined(__APPLE__)) || defined(HOST_SOLARIS) +#include +#define fabsf(f) ((float)fabs(f)) +#else +#include +#endif + +/* + * Define some C99-7.12.3 classification macros and + * some C99-.12.4 for Solaris systems OS less than 10, + * or Solaris 10 systems running GCC 3.x or less. + * Solaris 10 with GCC4 does not need these macros as they + * are defined in with a compiler directive + */ +#if defined(HOST_SOLARIS) && (( HOST_SOLARIS <= 9 ) || ( ( HOST_SOLARIS >= 10 ) && ( __GNUC__ <= 4) )) +/* + * C99 7.12.3 classification macros + * and + * C99 7.12.14 comparison macros + * + * ... do not work on Solaris 10 using GNU CC 3.4.x. + * Try to workaround the missing / broken C99 math macros. + */ + +#define isnormal(x) (fpclass(x) >= FP_NZERO) +#define isgreater(x, y) ((!unordered(x, y)) && ((x) > (y))) +#define isgreaterequal(x, y) ((!unordered(x, y)) && ((x) >= (y))) +#define isless(x, y) ((!unordered(x, y)) && ((x) < (y))) +#define islessequal(x, y) ((!unordered(x, y)) && ((x) <= (y))) +#define isunordered(x,y) unordered(x, y) +#endif + +typedef float float32; +typedef double float64; +#ifdef FLOATX80 +typedef long double floatx80; +#endif + +typedef union { + float32 f; + uint32_t i; +} float32u; +typedef union { + float64 f; + uint64_t i; +} float64u; +#ifdef FLOATX80 +typedef union { + floatx80 f; + struct { + uint64_t low; + uint16_t high; + } i; +} floatx80u; +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE floating-point rounding mode. +*----------------------------------------------------------------------------*/ +#if (defined(_BSD) && !defined(__APPLE__)) || defined(HOST_SOLARIS) +enum { + float_round_nearest_even = FP_RN, + float_round_down = FP_RM, + float_round_up = FP_RP, + float_round_to_zero = FP_RZ +}; +#elif defined(__arm__) +enum { + float_round_nearest_even = 0, + float_round_down = 1, + float_round_up = 2, + float_round_to_zero = 3 +}; +#else +enum { + float_round_nearest_even = FE_TONEAREST, + float_round_down = FE_DOWNWARD, + float_round_up = FE_UPWARD, + float_round_to_zero = FE_TOWARDZERO +}; +#endif + +typedef struct float_status { + signed char float_rounding_mode; +#ifdef FLOATX80 + signed char floatx80_rounding_precision; +#endif +} float_status; + +void set_float_rounding_mode(int val STATUS_PARAM); +#ifdef FLOATX80 +void set_floatx80_rounding_precision(int val STATUS_PARAM); +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE integer-to-floating-point conversion routines. +*----------------------------------------------------------------------------*/ +float32 int32_to_float32( int STATUS_PARAM); +float64 int32_to_float64( int STATUS_PARAM); +#ifdef FLOATX80 +floatx80 int32_to_floatx80( int STATUS_PARAM); +#endif +#ifdef FLOAT128 +float128 int32_to_float128( int STATUS_PARAM); +#endif +float32 int64_to_float32( int64_t STATUS_PARAM); +float64 int64_to_float64( int64_t STATUS_PARAM); +#ifdef FLOATX80 +floatx80 int64_to_floatx80( int64_t STATUS_PARAM); +#endif +#ifdef FLOAT128 +float128 int64_to_float128( int64_t STATUS_PARAM); +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE single-precision conversion routines. +*----------------------------------------------------------------------------*/ +int float32_to_int32( float32 STATUS_PARAM); +int float32_to_int32_round_to_zero( float32 STATUS_PARAM); +int64_t float32_to_int64( float32 STATUS_PARAM); +int64_t float32_to_int64_round_to_zero( float32 STATUS_PARAM); +float64 float32_to_float64( float32 STATUS_PARAM); +#ifdef FLOATX80 +floatx80 float32_to_floatx80( float32 STATUS_PARAM); +#endif +#ifdef FLOAT128 +float128 float32_to_float128( float32 STATUS_PARAM); +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE single-precision operations. +*----------------------------------------------------------------------------*/ +float32 float32_round_to_int( float32 STATUS_PARAM); +INLINE float32 float32_add( float32 a, float32 b STATUS_PARAM) +{ + return a + b; +} +INLINE float32 float32_sub( float32 a, float32 b STATUS_PARAM) +{ + return a - b; +} +INLINE float32 float32_mul( float32 a, float32 b STATUS_PARAM) +{ + return a * b; +} +INLINE float32 float32_div( float32 a, float32 b STATUS_PARAM) +{ + return a / b; +} +float32 float32_rem( float32, float32 STATUS_PARAM); +float32 float32_sqrt( float32 STATUS_PARAM); +INLINE char float32_eq( float32 a, float32 b STATUS_PARAM) +{ + return a == b; +} +INLINE char float32_le( float32 a, float32 b STATUS_PARAM) +{ + return a <= b; +} +INLINE char float32_lt( float32 a, float32 b STATUS_PARAM) +{ + return a < b; +} +INLINE char float32_eq_signaling( float32 a, float32 b STATUS_PARAM) +{ + return a <= b && a >= b; +} +INLINE char float32_le_quiet( float32 a, float32 b STATUS_PARAM) +{ + return islessequal(a, b); +} +INLINE char float32_lt_quiet( float32 a, float32 b STATUS_PARAM) +{ + return isless(a, b); +} +INLINE char float32_unordered( float32 a, float32 b STATUS_PARAM) +{ + return isunordered(a, b); + +} +char float32_compare( float32, float32 STATUS_PARAM ); +char float32_compare_quiet( float32, float32 STATUS_PARAM ); +char float32_is_signaling_nan( float32 ); + +INLINE float32 float32_abs(float32 a) +{ + return fabsf(a); +} + +INLINE float32 float32_chs(float32 a) +{ + return -a; +} + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE double-precision conversion routines. +*----------------------------------------------------------------------------*/ +int float64_to_int32( float64 STATUS_PARAM ); +int float64_to_int32_round_to_zero( float64 STATUS_PARAM ); +int64_t float64_to_int64( float64 STATUS_PARAM ); +int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM ); +float32 float64_to_float32( float64 STATUS_PARAM ); +#ifdef FLOATX80 +floatx80 float64_to_floatx80( float64 STATUS_PARAM ); +#endif +#ifdef FLOAT128 +float128 float64_to_float128( float64 STATUS_PARAM ); +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE double-precision operations. +*----------------------------------------------------------------------------*/ +float64 float64_round_to_int( float64 STATUS_PARAM ); +INLINE float64 float64_add( float64 a, float64 b STATUS_PARAM) +{ + return a + b; +} +INLINE float64 float64_sub( float64 a, float64 b STATUS_PARAM) +{ + return a - b; +} +INLINE float64 float64_mul( float64 a, float64 b STATUS_PARAM) +{ + return a * b; +} +INLINE float64 float64_div( float64 a, float64 b STATUS_PARAM) +{ + return a / b; +} +float64 float64_rem( float64, float64 STATUS_PARAM ); +float64 float64_sqrt( float64 STATUS_PARAM ); +INLINE char float64_eq( float64 a, float64 b STATUS_PARAM) +{ + return a == b; +} +INLINE char float64_le( float64 a, float64 b STATUS_PARAM) +{ + return a <= b; +} +INLINE char float64_lt( float64 a, float64 b STATUS_PARAM) +{ + return a < b; +} +INLINE char float64_eq_signaling( float64 a, float64 b STATUS_PARAM) +{ + return a <= b && a >= b; +} +INLINE char float64_le_quiet( float64 a, float64 b STATUS_PARAM) +{ + return islessequal(a, b); +} +INLINE char float64_lt_quiet( float64 a, float64 b STATUS_PARAM) +{ + return isless(a, b); + +} +INLINE char float64_unordered( float64 a, float64 b STATUS_PARAM) +{ + return isunordered(a, b); + +} +char float64_compare( float64, float64 STATUS_PARAM ); +char float64_compare_quiet( float64, float64 STATUS_PARAM ); +char float64_is_signaling_nan( float64 ); + +INLINE float64 float64_abs(float64 a) +{ + return fabs(a); +} + +INLINE float64 float64_chs(float64 a) +{ + return -a; +} + +#ifdef FLOATX80 + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE extended double-precision conversion routines. +*----------------------------------------------------------------------------*/ +int floatx80_to_int32( floatx80 STATUS_PARAM ); +int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM ); +int64_t floatx80_to_int64( floatx80 STATUS_PARAM); +int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM); +float32 floatx80_to_float32( floatx80 STATUS_PARAM ); +float64 floatx80_to_float64( floatx80 STATUS_PARAM ); +#ifdef FLOAT128 +float128 floatx80_to_float128( floatx80 STATUS_PARAM ); +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE extended double-precision operations. +*----------------------------------------------------------------------------*/ +floatx80 floatx80_round_to_int( floatx80 STATUS_PARAM ); +INLINE floatx80 floatx80_add( floatx80 a, floatx80 b STATUS_PARAM) +{ + return a + b; +} +INLINE floatx80 floatx80_sub( floatx80 a, floatx80 b STATUS_PARAM) +{ + return a - b; +} +INLINE floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM) +{ + return a * b; +} +INLINE floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM) +{ + return a / b; +} +floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM ); +floatx80 floatx80_sqrt( floatx80 STATUS_PARAM ); +INLINE char floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM) +{ + return a == b; +} +INLINE char floatx80_le( floatx80 a, floatx80 b STATUS_PARAM) +{ + return a <= b; +} +INLINE char floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM) +{ + return a < b; +} +INLINE char floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM) +{ + return a <= b && a >= b; +} +INLINE char floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM) +{ + return islessequal(a, b); +} +INLINE char floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM) +{ + return isless(a, b); + +} +INLINE char floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM) +{ + return isunordered(a, b); + +} +char floatx80_compare( floatx80, floatx80 STATUS_PARAM ); +char floatx80_compare_quiet( floatx80, floatx80 STATUS_PARAM ); +char floatx80_is_signaling_nan( floatx80 ); + +INLINE floatx80 floatx80_abs(floatx80 a) +{ + return fabsl(a); +} + +INLINE floatx80 floatx80_chs(floatx80 a) +{ + return -a; +} +#endif diff --git a/tools/ioemu/fpu/softfloat-specialize.h b/tools/ioemu/fpu/softfloat-specialize.h new file mode 100644 index 0000000000..d430f58a71 --- /dev/null +++ b/tools/ioemu/fpu/softfloat-specialize.h @@ -0,0 +1,464 @@ + +/*============================================================================ + +This C source fragment is part of the SoftFloat IEC/IEEE Floating-point +Arithmetic Package, Release 2b. + +Written by John R. Hauser. This work was made possible in part by the +International Computer Science Institute, located at Suite 600, 1947 Center +Street, Berkeley, California 94704. Funding was partially provided by the +National Science Foundation under grant MIP-9311980. The original version +of this code was written as part of a project to build a fixed-point vector +processor in collaboration with the University of California at Berkeley, +overseen by Profs. Nelson Morgan and John Wawrzynek. More information +is available through the Web page `http://www.cs.berkeley.edu/~jhauser/ +arithmetic/SoftFloat.html'. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has +been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES +RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS +AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES, +COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE +EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE +INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR +OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE. + +Derivative works are acceptable, even for commercial purposes, so long as +(1) the source code for the derivative work includes prominent notice that +the work is derivative, and (2) the source code includes prominent notice with +these four paragraphs for those parts of this code that are retained. + +=============================================================================*/ + +/*---------------------------------------------------------------------------- +| Underflow tininess-detection mode, statically initialized to default value. +| (The declaration in `softfloat.h' must match the `int8' type here.) +*----------------------------------------------------------------------------*/ +int8 float_detect_tininess = float_tininess_after_rounding; + +/*---------------------------------------------------------------------------- +| Raises the exceptions specified by `flags'. Floating-point traps can be +| defined here if desired. It is currently not possible for such a trap +| to substitute a result value. If traps are not implemented, this routine +| should be simply `float_exception_flags |= flags;'. +*----------------------------------------------------------------------------*/ + +void float_raise( int8 flags STATUS_PARAM ) +{ + + STATUS(float_exception_flags) |= flags; + +} + +/*---------------------------------------------------------------------------- +| Internal canonical NaN format. +*----------------------------------------------------------------------------*/ +typedef struct { + flag sign; + bits64 high, low; +} commonNaNT; + +/*---------------------------------------------------------------------------- +| The pattern for a default generated single-precision NaN. +*----------------------------------------------------------------------------*/ +#define float32_default_nan 0xFFC00000 + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is a NaN; +| otherwise returns 0. +*----------------------------------------------------------------------------*/ + +flag float32_is_nan( float32 a ) +{ + + return ( 0xFF000000 < (bits32) ( a<<1 ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is a signaling +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +flag float32_is_signaling_nan( float32 a ) +{ + + return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point NaN +| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid +| exception is raised. +*----------------------------------------------------------------------------*/ + +static commonNaNT float32ToCommonNaN( float32 a STATUS_PARAM ) +{ + commonNaNT z; + + if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR ); + z.sign = a>>31; + z.low = 0; + z.high = ( (bits64) a )<<41; + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the canonical NaN `a' to the single- +| precision floating-point format. +*----------------------------------------------------------------------------*/ + +static float32 commonNaNToFloat32( commonNaNT a ) +{ + + return ( ( (bits32) a.sign )<<31 ) | 0x7FC00000 | ( a.high>>41 ); + +} + +/*---------------------------------------------------------------------------- +| Takes two single-precision floating-point values `a' and `b', one of which +| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a +| signaling NaN, the invalid exception is raised. +*----------------------------------------------------------------------------*/ + +static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM) +{ + flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + + aIsNaN = float32_is_nan( a ); + aIsSignalingNaN = float32_is_signaling_nan( a ); + bIsNaN = float32_is_nan( b ); + bIsSignalingNaN = float32_is_signaling_nan( b ); + a |= 0x00400000; + b |= 0x00400000; + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); + if ( aIsSignalingNaN ) { + if ( bIsSignalingNaN ) goto returnLargerSignificand; + return bIsNaN ? b : a; + } + else if ( aIsNaN ) { + if ( bIsSignalingNaN | ! bIsNaN ) return a; + returnLargerSignificand: + if ( (bits32) ( a<<1 ) < (bits32) ( b<<1 ) ) return b; + if ( (bits32) ( b<<1 ) < (bits32) ( a<<1 ) ) return a; + return ( a < b ) ? a : b; + } + else { + return b; + } + +} + +/*---------------------------------------------------------------------------- +| The pattern for a default generated double-precision NaN. +*----------------------------------------------------------------------------*/ +#define float64_default_nan LIT64( 0xFFF8000000000000 ) + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is a NaN; +| otherwise returns 0. +*----------------------------------------------------------------------------*/ + +flag float64_is_nan( float64 a ) +{ + + return ( LIT64( 0xFFE0000000000000 ) < (bits64) ( a<<1 ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is a signaling +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +flag float64_is_signaling_nan( float64 a ) +{ + + return + ( ( ( a>>51 ) & 0xFFF ) == 0xFFE ) + && ( a & LIT64( 0x0007FFFFFFFFFFFF ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point NaN +| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid +| exception is raised. +*----------------------------------------------------------------------------*/ + +static commonNaNT float64ToCommonNaN( float64 a STATUS_PARAM) +{ + commonNaNT z; + + if ( float64_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR); + z.sign = a>>63; + z.low = 0; + z.high = a<<12; + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the canonical NaN `a' to the double- +| precision floating-point format. +*----------------------------------------------------------------------------*/ + +static float64 commonNaNToFloat64( commonNaNT a ) +{ + + return + ( ( (bits64) a.sign )<<63 ) + | LIT64( 0x7FF8000000000000 ) + | ( a.high>>12 ); + +} + +/*---------------------------------------------------------------------------- +| Takes two double-precision floating-point values `a' and `b', one of which +| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a +| signaling NaN, the invalid exception is raised. +*----------------------------------------------------------------------------*/ + +static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM) +{ + flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + + aIsNaN = float64_is_nan( a ); + aIsSignalingNaN = float64_is_signaling_nan( a ); + bIsNaN = float64_is_nan( b ); + bIsSignalingNaN = float64_is_signaling_nan( b ); + a |= LIT64( 0x0008000000000000 ); + b |= LIT64( 0x0008000000000000 ); + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); + if ( aIsSignalingNaN ) { + if ( bIsSignalingNaN ) goto returnLargerSignificand; + return bIsNaN ? b : a; + } + else if ( aIsNaN ) { + if ( bIsSignalingNaN | ! bIsNaN ) return a; + returnLargerSignificand: + if ( (bits64) ( a<<1 ) < (bits64) ( b<<1 ) ) return b; + if ( (bits64) ( b<<1 ) < (bits64) ( a<<1 ) ) return a; + return ( a < b ) ? a : b; + } + else { + return b; + } + +} + +#ifdef FLOATX80 + +/*---------------------------------------------------------------------------- +| The pattern for a default generated extended double-precision NaN. The +| `high' and `low' values hold the most- and least-significant bits, +| respectively. +*----------------------------------------------------------------------------*/ +#define floatx80_default_nan_high 0xFFFF +#define floatx80_default_nan_low LIT64( 0xC000000000000000 ) + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is a +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +flag floatx80_is_nan( floatx80 a ) +{ + + return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is a +| signaling NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +flag floatx80_is_signaling_nan( floatx80 a ) +{ + bits64 aLow; + + aLow = a.low & ~ LIT64( 0x4000000000000000 ); + return + ( ( a.high & 0x7FFF ) == 0x7FFF ) + && (bits64) ( aLow<<1 ) + && ( a.low == aLow ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point NaN `a' to the canonical NaN format. If `a' is a signaling NaN, the +| invalid exception is raised. +*----------------------------------------------------------------------------*/ + +static commonNaNT floatx80ToCommonNaN( floatx80 a STATUS_PARAM) +{ + commonNaNT z; + + if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR); + z.sign = a.high>>15; + z.low = 0; + z.high = a.low<<1; + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the canonical NaN `a' to the extended +| double-precision floating-point format. +*----------------------------------------------------------------------------*/ + +static floatx80 commonNaNToFloatx80( commonNaNT a ) +{ + floatx80 z; + + z.low = LIT64( 0xC000000000000000 ) | ( a.high>>1 ); + z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF; + return z; + +} + +/*---------------------------------------------------------------------------- +| Takes two extended double-precision floating-point values `a' and `b', one +| of which is a NaN, and returns the appropriate NaN result. If either `a' or +| `b' is a signaling NaN, the invalid exception is raised. +*----------------------------------------------------------------------------*/ + +static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM) +{ + flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + + aIsNaN = floatx80_is_nan( a ); + aIsSignalingNaN = floatx80_is_signaling_nan( a ); + bIsNaN = floatx80_is_nan( b ); + bIsSignalingNaN = floatx80_is_signaling_nan( b ); + a.low |= LIT64( 0xC000000000000000 ); + b.low |= LIT64( 0xC000000000000000 ); + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); + if ( aIsSignalingNaN ) { + if ( bIsSignalingNaN ) goto returnLargerSignificand; + return bIsNaN ? b : a; + } + else if ( aIsNaN ) { + if ( bIsSignalingNaN | ! bIsNaN ) return a; + returnLargerSignificand: + if ( a.low < b.low ) return b; + if ( b.low < a.low ) return a; + return ( a.high < b.high ) ? a : b; + } + else { + return b; + } + +} + +#endif + +#ifdef FLOAT128 + +/*---------------------------------------------------------------------------- +| The pattern for a default generated quadruple-precision NaN. The `high' and +| `low' values hold the most- and least-significant bits, respectively. +*----------------------------------------------------------------------------*/ +#define float128_default_nan_high LIT64( 0xFFFF800000000000 ) +#define float128_default_nan_low LIT64( 0x0000000000000000 ) + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is a NaN; +| otherwise returns 0. +*----------------------------------------------------------------------------*/ + +flag float128_is_nan( float128 a ) +{ + + return + ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) ) + && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is a +| signaling NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +flag float128_is_signaling_nan( float128 a ) +{ + + return + ( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE ) + && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point NaN +| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid +| exception is raised. +*----------------------------------------------------------------------------*/ + +static commonNaNT float128ToCommonNaN( float128 a STATUS_PARAM) +{ + commonNaNT z; + + if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR); + z.sign = a.high>>63; + shortShift128Left( a.high, a.low, 16, &z.high, &z.low ); + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the canonical NaN `a' to the quadruple- +| precision floating-point format. +*----------------------------------------------------------------------------*/ + +static float128 commonNaNToFloat128( commonNaNT a ) +{ + float128 z; + + shift128Right( a.high, a.low, 16, &z.high, &z.low ); + z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF800000000000 ); + return z; + +} + +/*---------------------------------------------------------------------------- +| Takes two quadruple-precision floating-point values `a' and `b', one of +| which is a NaN, and returns the appropriate NaN result. If either `a' or +| `b' is a signaling NaN, the invalid exception is raised. +*----------------------------------------------------------------------------*/ + +static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM) +{ + flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + + aIsNaN = float128_is_nan( a ); + aIsSignalingNaN = float128_is_signaling_nan( a ); + bIsNaN = float128_is_nan( b ); + bIsSignalingNaN = float128_is_signaling_nan( b ); + a.high |= LIT64( 0x0000800000000000 ); + b.high |= LIT64( 0x0000800000000000 ); + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); + if ( aIsSignalingNaN ) { + if ( bIsSignalingNaN ) goto returnLargerSignificand; + return bIsNaN ? b : a; + } + else if ( aIsNaN ) { + if ( bIsSignalingNaN | ! bIsNaN ) return a; + returnLargerSignificand: + if ( lt128( a.high<<1, a.low, b.high<<1, b.low ) ) return b; + if ( lt128( b.high<<1, b.low, a.high<<1, a.low ) ) return a; + return ( a.high < b.high ) ? a : b; + } + else { + return b; + } + +} + +#endif + diff --git a/tools/ioemu/fpu/softfloat.c b/tools/ioemu/fpu/softfloat.c new file mode 100644 index 0000000000..5e846200ae --- /dev/null +++ b/tools/ioemu/fpu/softfloat.c @@ -0,0 +1,5320 @@ + +/*============================================================================ + +This C source file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic +Package, Release 2b. + +Written by John R. Hauser. This work was made possible in part by the +International Computer Science Institute, located at Suite 600, 1947 Center +Street, Berkeley, California 94704. Funding was partially provided by the +National Science Foundation under grant MIP-9311980. The original version +of this code was written as part of a project to build a fixed-point vector +processor in collaboration with the University of California at Berkeley, +overseen by Profs. Nelson Morgan and John Wawrzynek. More information +is available through the Web page `http://www.cs.berkeley.edu/~jhauser/ +arithmetic/SoftFloat.html'. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has +been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES +RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS +AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES, +COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE +EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE +INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR +OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE. + +Derivative works are acceptable, even for commercial purposes, so long as +(1) the source code for the derivative work includes prominent notice that +the work is derivative, and (2) the source code includes prominent notice with +these four paragraphs for those parts of this code that are retained. + +=============================================================================*/ + +#include "softfloat.h" + +/*---------------------------------------------------------------------------- +| Primitive arithmetic functions, including multi-word arithmetic, and +| division and square root approximations. (Can be specialized to target if +| desired.) +*----------------------------------------------------------------------------*/ +#include "softfloat-macros.h" + +/*---------------------------------------------------------------------------- +| Functions and definitions to determine: (1) whether tininess for underflow +| is detected before or after rounding by default, (2) what (if anything) +| happens when exceptions are raised, (3) how signaling NaNs are distinguished +| from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs +| are propagated from function inputs to output. These details are target- +| specific. +*----------------------------------------------------------------------------*/ +#include "softfloat-specialize.h" + +void set_float_rounding_mode(int val STATUS_PARAM) +{ + STATUS(float_rounding_mode) = val; +} + +void set_float_exception_flags(int val STATUS_PARAM) +{ + STATUS(float_exception_flags) = val; +} + +#ifdef FLOATX80 +void set_floatx80_rounding_precision(int val STATUS_PARAM) +{ + STATUS(floatx80_rounding_precision) = val; +} +#endif + +/*---------------------------------------------------------------------------- +| Takes a 64-bit fixed-point value `absZ' with binary point between bits 6 +| and 7, and returns the properly rounded 32-bit integer corresponding to the +| input. If `zSign' is 1, the input is negated before being converted to an +| integer. Bit 63 of `absZ' must be zero. Ordinarily, the fixed-point input +| is simply rounded to an integer, with the inexact exception raised if the +| input cannot be represented exactly as an integer. However, if the fixed- +| point input is too large, the invalid exception is raised and the largest +| positive or negative integer is returned. +*----------------------------------------------------------------------------*/ + +static int32 roundAndPackInt32( flag zSign, bits64 absZ STATUS_PARAM) +{ + int8 roundingMode; + flag roundNearestEven; + int8 roundIncrement, roundBits; + int32 z; + + roundingMode = STATUS(float_rounding_mode); + roundNearestEven = ( roundingMode == float_round_nearest_even ); + roundIncrement = 0x40; + if ( ! roundNearestEven ) { + if ( roundingMode == float_round_to_zero ) { + roundIncrement = 0; + } + else { + roundIncrement = 0x7F; + if ( zSign ) { + if ( roundingMode == float_round_up ) roundIncrement = 0; + } + else { + if ( roundingMode == float_round_down ) roundIncrement = 0; + } + } + } + roundBits = absZ & 0x7F; + absZ = ( absZ + roundIncrement )>>7; + absZ &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven ); + z = absZ; + if ( zSign ) z = - z; + if ( ( absZ>>32 ) || ( z && ( ( z < 0 ) ^ zSign ) ) ) { + float_raise( float_flag_invalid STATUS_VAR); + return zSign ? (sbits32) 0x80000000 : 0x7FFFFFFF; + } + if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact; + return z; + +} + +/*---------------------------------------------------------------------------- +| Takes the 128-bit fixed-point value formed by concatenating `absZ0' and +| `absZ1', with binary point between bits 63 and 64 (between the input words), +| and returns the properly rounded 64-bit integer corresponding to the input. +| If `zSign' is 1, the input is negated before being converted to an integer. +| Ordinarily, the fixed-point input is simply rounded to an integer, with +| the inexact exception raised if the input cannot be represented exactly as +| an integer. However, if the fixed-point input is too large, the invalid +| exception is raised and the largest positive or negative integer is +| returned. +*----------------------------------------------------------------------------*/ + +static int64 roundAndPackInt64( flag zSign, bits64 absZ0, bits64 absZ1 STATUS_PARAM) +{ + int8 roundingMode; + flag roundNearestEven, increment; + int64 z; + + roundingMode = STATUS(float_rounding_mode); + roundNearestEven = ( roundingMode == float_round_nearest_even ); + increment = ( (sbits64) absZ1 < 0 ); + if ( ! roundNearestEven ) { + if ( roundingMode == float_round_to_zero ) { + increment = 0; + } + else { + if ( zSign ) { + increment = ( roundingMode == float_round_down ) && absZ1; + } + else { + increment = ( roundingMode == float_round_up ) && absZ1; + } + } + } + if ( increment ) { + ++absZ0; + if ( absZ0 == 0 ) goto overflow; + absZ0 &= ~ ( ( (bits64) ( absZ1<<1 ) == 0 ) & roundNearestEven ); + } + z = absZ0; + if ( zSign ) z = - z; + if ( z && ( ( z < 0 ) ^ zSign ) ) { + overflow: + float_raise( float_flag_invalid STATUS_VAR); + return + zSign ? (sbits64) LIT64( 0x8000000000000000 ) + : LIT64( 0x7FFFFFFFFFFFFFFF ); + } + if ( absZ1 ) STATUS(float_exception_flags) |= float_flag_inexact; + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the fraction bits of the single-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE bits32 extractFloat32Frac( float32 a ) +{ + + return a & 0x007FFFFF; + +} + +/*---------------------------------------------------------------------------- +| Returns the exponent bits of the single-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE int16 extractFloat32Exp( float32 a ) +{ + + return ( a>>23 ) & 0xFF; + +} + +/*---------------------------------------------------------------------------- +| Returns the sign bit of the single-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE flag extractFloat32Sign( float32 a ) +{ + + return a>>31; + +} + +/*---------------------------------------------------------------------------- +| Normalizes the subnormal single-precision floating-point value represented +| by the denormalized significand `aSig'. The normalized exponent and +| significand are stored at the locations pointed to by `zExpPtr' and +| `zSigPtr', respectively. +*----------------------------------------------------------------------------*/ + +static void + normalizeFloat32Subnormal( bits32 aSig, int16 *zExpPtr, bits32 *zSigPtr ) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros32( aSig ) - 8; + *zSigPtr = aSig<>7; + zSig &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven ); + if ( zSig == 0 ) zExp = 0; + return packFloat32( zSign, zExp, zSig ); + +} + +/*---------------------------------------------------------------------------- +| Takes an abstract floating-point value having sign `zSign', exponent `zExp', +| and significand `zSig', and returns the proper single-precision floating- +| point value corresponding to the abstract input. This routine is just like +| `roundAndPackFloat32' except that `zSig' does not have to be normalized. +| Bit 31 of `zSig' must be zero, and `zExp' must be 1 less than the ``true'' +| floating-point exponent. +*----------------------------------------------------------------------------*/ + +static float32 + normalizeRoundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig STATUS_PARAM) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros32( zSig ) - 1; + return roundAndPackFloat32( zSign, zExp - shiftCount, zSig<>52 ) & 0x7FF; + +} + +/*---------------------------------------------------------------------------- +| Returns the sign bit of the double-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE flag extractFloat64Sign( float64 a ) +{ + + return a>>63; + +} + +/*---------------------------------------------------------------------------- +| Normalizes the subnormal double-precision floating-point value represented +| by the denormalized significand `aSig'. The normalized exponent and +| significand are stored at the locations pointed to by `zExpPtr' and +| `zSigPtr', respectively. +*----------------------------------------------------------------------------*/ + +static void + normalizeFloat64Subnormal( bits64 aSig, int16 *zExpPtr, bits64 *zSigPtr ) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros64( aSig ) - 11; + *zSigPtr = aSig<>10; + zSig &= ~ ( ( ( roundBits ^ 0x200 ) == 0 ) & roundNearestEven ); + if ( zSig == 0 ) zExp = 0; + return packFloat64( zSign, zExp, zSig ); + +} + +/*---------------------------------------------------------------------------- +| Takes an abstract floating-point value having sign `zSign', exponent `zExp', +| and significand `zSig', and returns the proper double-precision floating- +| point value corresponding to the abstract input. This routine is just like +| `roundAndPackFloat64' except that `zSig' does not have to be normalized. +| Bit 63 of `zSig' must be zero, and `zExp' must be 1 less than the ``true'' +| floating-point exponent. +*----------------------------------------------------------------------------*/ + +static float64 + normalizeRoundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig STATUS_PARAM) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros64( zSig ) - 1; + return roundAndPackFloat64( zSign, zExp - shiftCount, zSig<>15; + +} + +/*---------------------------------------------------------------------------- +| Normalizes the subnormal extended double-precision floating-point value +| represented by the denormalized significand `aSig'. The normalized exponent +| and significand are stored at the locations pointed to by `zExpPtr' and +| `zSigPtr', respectively. +*----------------------------------------------------------------------------*/ + +static void + normalizeFloatx80Subnormal( bits64 aSig, int32 *zExpPtr, bits64 *zSigPtr ) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros64( aSig ); + *zSigPtr = aSig<>48 ) & 0x7FFF; + +} + +/*---------------------------------------------------------------------------- +| Returns the sign bit of the quadruple-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE flag extractFloat128Sign( float128 a ) +{ + + return a.high>>63; + +} + +/*---------------------------------------------------------------------------- +| Normalizes the subnormal quadruple-precision floating-point value +| represented by the denormalized significand formed by the concatenation of +| `aSig0' and `aSig1'. The normalized exponent is stored at the location +| pointed to by `zExpPtr'. The most significant 49 bits of the normalized +| significand are stored at the location pointed to by `zSig0Ptr', and the +| least significant 64 bits of the normalized significand are stored at the +| location pointed to by `zSig1Ptr'. +*----------------------------------------------------------------------------*/ + +static void + normalizeFloat128Subnormal( + bits64 aSig0, + bits64 aSig1, + int32 *zExpPtr, + bits64 *zSig0Ptr, + bits64 *zSig1Ptr + ) +{ + int8 shiftCount; + + if ( aSig0 == 0 ) { + shiftCount = countLeadingZeros64( aSig1 ) - 15; + if ( shiftCount < 0 ) { + *zSig0Ptr = aSig1>>( - shiftCount ); + *zSig1Ptr = aSig1<<( shiftCount & 63 ); + } + else { + *zSig0Ptr = aSig1<>( - shiftCount ); + if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + if ( aSign ) z = - z; + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the 64-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic---which means in particular that the conversion is rounded +| according to the current rounding mode. If `a' is a NaN, the largest +| positive integer is returned. Otherwise, if the conversion overflows, the +| largest integer with the same sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int64 float32_to_int64( float32 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp, shiftCount; + bits32 aSig; + bits64 aSig64, aSigExtra; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + shiftCount = 0xBE - aExp; + if ( shiftCount < 0 ) { + float_raise( float_flag_invalid STATUS_VAR); + if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) { + return LIT64( 0x7FFFFFFFFFFFFFFF ); + } + return (sbits64) LIT64( 0x8000000000000000 ); + } + if ( aExp ) aSig |= 0x00800000; + aSig64 = aSig; + aSig64 <<= 40; + shift64ExtraRightJamming( aSig64, 0, shiftCount, &aSig64, &aSigExtra ); + return roundAndPackInt64( aSign, aSig64, aSigExtra STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the 64-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. If +| `a' is a NaN, the largest positive integer is returned. Otherwise, if the +| conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int64 float32_to_int64_round_to_zero( float32 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp, shiftCount; + bits32 aSig; + bits64 aSig64; + int64 z; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + shiftCount = aExp - 0xBE; + if ( 0 <= shiftCount ) { + if ( a != 0xDF000000 ) { + float_raise( float_flag_invalid STATUS_VAR); + if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) { + return LIT64( 0x7FFFFFFFFFFFFFFF ); + } + } + return (sbits64) LIT64( 0x8000000000000000 ); + } + else if ( aExp <= 0x7E ) { + if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact; + return 0; + } + aSig64 = aSig | 0x00800000; + aSig64 <<= 40; + z = aSig64>>( - shiftCount ); + if ( (bits64) ( aSig64<<( shiftCount & 63 ) ) ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + if ( aSign ) z = - z; + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the double-precision floating-point format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float32_to_float64( float32 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp; + bits32 aSig; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + if ( aExp == 0xFF ) { + if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a STATUS_VAR )); + return packFloat64( aSign, 0x7FF, 0 ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloat64( aSign, 0, 0 ); + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + --aExp; + } + return packFloat64( aSign, aExp + 0x380, ( (bits64) aSig )<<29 ); + +} + +#ifdef FLOATX80 + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the extended double-precision floating-point format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 float32_to_floatx80( float32 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp; + bits32 aSig; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + if ( aExp == 0xFF ) { + if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a STATUS_VAR ) ); + return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 ); + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + } + aSig |= 0x00800000; + return packFloatx80( aSign, aExp + 0x3F80, ( (bits64) aSig )<<40 ); + +} + +#endif + +#ifdef FLOAT128 + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the double-precision floating-point format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float32_to_float128( float32 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp; + bits32 aSig; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + if ( aExp == 0xFF ) { + if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a STATUS_VAR ) ); + return packFloat128( aSign, 0x7FFF, 0, 0 ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 ); + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + --aExp; + } + return packFloat128( aSign, aExp + 0x3F80, ( (bits64) aSig )<<25, 0 ); + +} + +#endif + +/*---------------------------------------------------------------------------- +| Rounds the single-precision floating-point value `a' to an integer, and +| returns the result as a single-precision floating-point value. The +| operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_round_to_int( float32 a STATUS_PARAM) +{ + flag aSign; + int16 aExp; + bits32 lastBitMask, roundBitsMask; + int8 roundingMode; + float32 z; + + aExp = extractFloat32Exp( a ); + if ( 0x96 <= aExp ) { + if ( ( aExp == 0xFF ) && extractFloat32Frac( a ) ) { + return propagateFloat32NaN( a, a STATUS_VAR ); + } + return a; + } + if ( aExp <= 0x7E ) { + if ( (bits32) ( a<<1 ) == 0 ) return a; + STATUS(float_exception_flags) |= float_flag_inexact; + aSign = extractFloat32Sign( a ); + switch ( STATUS(float_rounding_mode) ) { + case float_round_nearest_even: + if ( ( aExp == 0x7E ) && extractFloat32Frac( a ) ) { + return packFloat32( aSign, 0x7F, 0 ); + } + break; + case float_round_down: + return aSign ? 0xBF800000 : 0; + case float_round_up: + return aSign ? 0x80000000 : 0x3F800000; + } + return packFloat32( aSign, 0, 0 ); + } + lastBitMask = 1; + lastBitMask <<= 0x96 - aExp; + roundBitsMask = lastBitMask - 1; + z = a; + roundingMode = STATUS(float_rounding_mode); + if ( roundingMode == float_round_nearest_even ) { + z += lastBitMask>>1; + if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask; + } + else if ( roundingMode != float_round_to_zero ) { + if ( extractFloat32Sign( z ) ^ ( roundingMode == float_round_up ) ) { + z += roundBitsMask; + } + } + z &= ~ roundBitsMask; + if ( z != a ) STATUS(float_exception_flags) |= float_flag_inexact; + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the absolute values of the single-precision +| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated +| before being returned. `zSign' is ignored if the result is a NaN. +| The addition is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) +{ + int16 aExp, bExp, zExp; + bits32 aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + bSig = extractFloat32Frac( b ); + bExp = extractFloat32Exp( b ); + expDiff = aExp - bExp; + aSig <<= 6; + bSig <<= 6; + if ( 0 < expDiff ) { + if ( aExp == 0xFF ) { + if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + return a; + } + if ( bExp == 0 ) { + --expDiff; + } + else { + bSig |= 0x20000000; + } + shift32RightJamming( bSig, expDiff, &bSig ); + zExp = aExp; + } + else if ( expDiff < 0 ) { + if ( bExp == 0xFF ) { + if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + return packFloat32( zSign, 0xFF, 0 ); + } + if ( aExp == 0 ) { + ++expDiff; + } + else { + aSig |= 0x20000000; + } + shift32RightJamming( aSig, - expDiff, &aSig ); + zExp = bExp; + } + else { + if ( aExp == 0xFF ) { + if ( aSig | bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + return a; + } + if ( aExp == 0 ) return packFloat32( zSign, 0, ( aSig + bSig )>>6 ); + zSig = 0x40000000 + aSig + bSig; + zExp = aExp; + goto roundAndPack; + } + aSig |= 0x20000000; + zSig = ( aSig + bSig )<<1; + --zExp; + if ( (sbits32) zSig < 0 ) { + zSig = aSig + bSig; + ++zExp; + } + roundAndPack: + return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the absolute values of the single- +| precision floating-point values `a' and `b'. If `zSign' is 1, the +| difference is negated before being returned. `zSign' is ignored if the +| result is a NaN. The subtraction is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM) +{ + int16 aExp, bExp, zExp; + bits32 aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + bSig = extractFloat32Frac( b ); + bExp = extractFloat32Exp( b ); + expDiff = aExp - bExp; + aSig <<= 7; + bSig <<= 7; + if ( 0 < expDiff ) goto aExpBigger; + if ( expDiff < 0 ) goto bExpBigger; + if ( aExp == 0xFF ) { + if ( aSig | bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + float_raise( float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + if ( aExp == 0 ) { + aExp = 1; + bExp = 1; + } + if ( bSig < aSig ) goto aBigger; + if ( aSig < bSig ) goto bBigger; + return packFloat32( STATUS(float_rounding_mode) == float_round_down, 0, 0 ); + bExpBigger: + if ( bExp == 0xFF ) { + if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + return packFloat32( zSign ^ 1, 0xFF, 0 ); + } + if ( aExp == 0 ) { + ++expDiff; + } + else { + aSig |= 0x40000000; + } + shift32RightJamming( aSig, - expDiff, &aSig ); + bSig |= 0x40000000; + bBigger: + zSig = bSig - aSig; + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; + aExpBigger: + if ( aExp == 0xFF ) { + if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + return a; + } + if ( bExp == 0 ) { + --expDiff; + } + else { + bSig |= 0x40000000; + } + shift32RightJamming( bSig, expDiff, &bSig ); + aSig |= 0x40000000; + aBigger: + zSig = aSig - bSig; + zExp = aExp; + normalizeRoundAndPack: + --zExp; + return normalizeRoundAndPackFloat32( zSign, zExp, zSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the single-precision floating-point values `a' +| and `b'. The operation is performed according to the IEC/IEEE Standard for +| Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_add( float32 a, float32 b STATUS_PARAM ) +{ + flag aSign, bSign; + + aSign = extractFloat32Sign( a ); + bSign = extractFloat32Sign( b ); + if ( aSign == bSign ) { + return addFloat32Sigs( a, b, aSign STATUS_VAR); + } + else { + return subFloat32Sigs( a, b, aSign STATUS_VAR ); + } + +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the single-precision floating-point values +| `a' and `b'. The operation is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_sub( float32 a, float32 b STATUS_PARAM ) +{ + flag aSign, bSign; + + aSign = extractFloat32Sign( a ); + bSign = extractFloat32Sign( b ); + if ( aSign == bSign ) { + return subFloat32Sigs( a, b, aSign STATUS_VAR ); + } + else { + return addFloat32Sigs( a, b, aSign STATUS_VAR ); + } + +} + +/*---------------------------------------------------------------------------- +| Returns the result of multiplying the single-precision floating-point values +| `a' and `b'. The operation is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_mul( float32 a, float32 b STATUS_PARAM ) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + bits32 aSig, bSig; + bits64 zSig64; + bits32 zSig; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + bSig = extractFloat32Frac( b ); + bExp = extractFloat32Exp( b ); + bSign = extractFloat32Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0xFF ) { + if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) { + return propagateFloat32NaN( a, b STATUS_VAR ); + } + if ( ( bExp | bSig ) == 0 ) { + float_raise( float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + return packFloat32( zSign, 0xFF, 0 ); + } + if ( bExp == 0xFF ) { + if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + if ( ( aExp | aSig ) == 0 ) { + float_raise( float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + return packFloat32( zSign, 0xFF, 0 ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloat32( zSign, 0, 0 ); + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + } + if ( bExp == 0 ) { + if ( bSig == 0 ) return packFloat32( zSign, 0, 0 ); + normalizeFloat32Subnormal( bSig, &bExp, &bSig ); + } + zExp = aExp + bExp - 0x7F; + aSig = ( aSig | 0x00800000 )<<7; + bSig = ( bSig | 0x00800000 )<<8; + shift64RightJamming( ( (bits64) aSig ) * bSig, 32, &zSig64 ); + zSig = zSig64; + if ( 0 <= (sbits32) ( zSig<<1 ) ) { + zSig <<= 1; + --zExp; + } + return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of dividing the single-precision floating-point value `a' +| by the corresponding value `b'. The operation is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_div( float32 a, float32 b STATUS_PARAM ) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + bits32 aSig, bSig, zSig; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + bSig = extractFloat32Frac( b ); + bExp = extractFloat32Exp( b ); + bSign = extractFloat32Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0xFF ) { + if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + if ( bExp == 0xFF ) { + if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + float_raise( float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + return packFloat32( zSign, 0xFF, 0 ); + } + if ( bExp == 0xFF ) { + if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + return packFloat32( zSign, 0, 0 ); + } + if ( bExp == 0 ) { + if ( bSig == 0 ) { + if ( ( aExp | aSig ) == 0 ) { + float_raise( float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + float_raise( float_flag_divbyzero STATUS_VAR); + return packFloat32( zSign, 0xFF, 0 ); + } + normalizeFloat32Subnormal( bSig, &bExp, &bSig ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloat32( zSign, 0, 0 ); + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + } + zExp = aExp - bExp + 0x7D; + aSig = ( aSig | 0x00800000 )<<7; + bSig = ( bSig | 0x00800000 )<<8; + if ( bSig <= ( aSig + aSig ) ) { + aSig >>= 1; + ++zExp; + } + zSig = ( ( (bits64) aSig )<<32 ) / bSig; + if ( ( zSig & 0x3F ) == 0 ) { + zSig |= ( (bits64) bSig * zSig != ( (bits64) aSig )<<32 ); + } + return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the remainder of the single-precision floating-point value `a' +| with respect to the corresponding value `b'. The operation is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_rem( float32 a, float32 b STATUS_PARAM ) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, expDiff; + bits32 aSig, bSig; + bits32 q; + bits64 aSig64, bSig64, q64; + bits32 alternateASig; + sbits32 sigMean; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + bSig = extractFloat32Frac( b ); + bExp = extractFloat32Exp( b ); + bSign = extractFloat32Sign( b ); + if ( aExp == 0xFF ) { + if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) { + return propagateFloat32NaN( a, b STATUS_VAR ); + } + float_raise( float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + if ( bExp == 0xFF ) { + if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR ); + return a; + } + if ( bExp == 0 ) { + if ( bSig == 0 ) { + float_raise( float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + normalizeFloat32Subnormal( bSig, &bExp, &bSig ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return a; + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + } + expDiff = aExp - bExp; + aSig |= 0x00800000; + bSig |= 0x00800000; + if ( expDiff < 32 ) { + aSig <<= 8; + bSig <<= 8; + if ( expDiff < 0 ) { + if ( expDiff < -1 ) return a; + aSig >>= 1; + } + q = ( bSig <= aSig ); + if ( q ) aSig -= bSig; + if ( 0 < expDiff ) { + q = ( ( (bits64) aSig )<<32 ) / bSig; + q >>= 32 - expDiff; + bSig >>= 2; + aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q; + } + else { + aSig >>= 2; + bSig >>= 2; + } + } + else { + if ( bSig <= aSig ) aSig -= bSig; + aSig64 = ( (bits64) aSig )<<40; + bSig64 = ( (bits64) bSig )<<40; + expDiff -= 64; + while ( 0 < expDiff ) { + q64 = estimateDiv128To64( aSig64, 0, bSig64 ); + q64 = ( 2 < q64 ) ? q64 - 2 : 0; + aSig64 = - ( ( bSig * q64 )<<38 ); + expDiff -= 62; + } + expDiff += 64; + q64 = estimateDiv128To64( aSig64, 0, bSig64 ); + q64 = ( 2 < q64 ) ? q64 - 2 : 0; + q = q64>>( 64 - expDiff ); + bSig <<= 6; + aSig = ( ( aSig64>>33 )<<( expDiff - 1 ) ) - bSig * q; + } + do { + alternateASig = aSig; + ++q; + aSig -= bSig; + } while ( 0 <= (sbits32) aSig ); + sigMean = aSig + alternateASig; + if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) { + aSig = alternateASig; + } + zSign = ( (sbits32) aSig < 0 ); + if ( zSign ) aSig = - aSig; + return normalizeRoundAndPackFloat32( aSign ^ zSign, bExp, aSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the square root of the single-precision floating-point value `a'. +| The operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_sqrt( float32 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp, zExp; + bits32 aSig, zSig; + bits64 rem, term; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + if ( aExp == 0xFF ) { + if ( aSig ) return propagateFloat32NaN( a, 0 STATUS_VAR ); + if ( ! aSign ) return a; + float_raise( float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + if ( aSign ) { + if ( ( aExp | aSig ) == 0 ) return a; + float_raise( float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return 0; + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + } + zExp = ( ( aExp - 0x7F )>>1 ) + 0x7E; + aSig = ( aSig | 0x00800000 )<<8; + zSig = estimateSqrt32( aExp, aSig ) + 2; + if ( ( zSig & 0x7F ) <= 5 ) { + if ( zSig < 2 ) { + zSig = 0x7FFFFFFF; + goto roundAndPack; + } + aSig >>= aExp & 1; + term = ( (bits64) zSig ) * zSig; + rem = ( ( (bits64) aSig )<<32 ) - term; + while ( (sbits64) rem < 0 ) { + --zSig; + rem += ( ( (bits64) zSig )<<1 ) | 1; + } + zSig |= ( rem != 0 ); + } + shift32RightJamming( zSig, 1, &zSig ); + roundAndPack: + return roundAndPackFloat32( 0, zExp, zSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is equal to +| the corresponding value `b', and 0 otherwise. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float32_eq( float32 a, float32 b STATUS_PARAM ) +{ + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + return ( a == b ) || ( (bits32) ( ( a | b )<<1 ) == 0 ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is less than +| or equal to the corresponding value `b', and 0 otherwise. The comparison +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float32_le( float32 a, float32 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloat32Sign( a ); + bSign = extractFloat32Sign( b ); + if ( aSign != bSign ) return aSign || ( (bits32) ( ( a | b )<<1 ) == 0 ); + return ( a == b ) || ( aSign ^ ( a < b ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is less than +| the corresponding value `b', and 0 otherwise. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float32_lt( float32 a, float32 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloat32Sign( a ); + bSign = extractFloat32Sign( b ); + if ( aSign != bSign ) return aSign && ( (bits32) ( ( a | b )<<1 ) != 0 ); + return ( a != b ) && ( aSign ^ ( a < b ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is equal to +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. Otherwise, the comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float32_eq_signaling( float32 a, float32 b STATUS_PARAM ) +{ + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 0; + } + return ( a == b ) || ( (bits32) ( ( a | b )<<1 ) == 0 ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is less than or +| equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not +| cause an exception. Otherwise, the comparison is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float32_le_quiet( float32 a, float32 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloat32Sign( a ); + bSign = extractFloat32Sign( b ); + if ( aSign != bSign ) return aSign || ( (bits32) ( ( a | b )<<1 ) == 0 ); + return ( a == b ) || ( aSign ^ ( a < b ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is less than +| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception. Otherwise, the comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float32_lt_quiet( float32 a, float32 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloat32Sign( a ); + bSign = extractFloat32Sign( b ); + if ( aSign != bSign ) return aSign && ( (bits32) ( ( a | b )<<1 ) != 0 ); + return ( a != b ) && ( aSign ^ ( a < b ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the 32-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic---which means in particular that the conversion is rounded +| according to the current rounding mode. If `a' is a NaN, the largest +| positive integer is returned. Otherwise, if the conversion overflows, the +| largest integer with the same sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int32 float64_to_int32( float64 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp, shiftCount; + bits64 aSig; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + if ( ( aExp == 0x7FF ) && aSig ) aSign = 0; + if ( aExp ) aSig |= LIT64( 0x0010000000000000 ); + shiftCount = 0x42C - aExp; + if ( 0 < shiftCount ) shift64RightJamming( aSig, shiftCount, &aSig ); + return roundAndPackInt32( aSign, aSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the 32-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. +| If `a' is a NaN, the largest positive integer is returned. Otherwise, if +| the conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp, shiftCount; + bits64 aSig, savedASig; + int32 z; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + if ( 0x41E < aExp ) { + if ( ( aExp == 0x7FF ) && aSig ) aSign = 0; + goto invalid; + } + else if ( aExp < 0x3FF ) { + if ( aExp || aSig ) STATUS(float_exception_flags) |= float_flag_inexact; + return 0; + } + aSig |= LIT64( 0x0010000000000000 ); + shiftCount = 0x433 - aExp; + savedASig = aSig; + aSig >>= shiftCount; + z = aSig; + if ( aSign ) z = - z; + if ( ( z < 0 ) ^ aSign ) { + invalid: + float_raise( float_flag_invalid STATUS_VAR); + return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF; + } + if ( ( aSig<>( - shiftCount ); + if ( (bits64) ( aSig<<( shiftCount & 63 ) ) ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + } + if ( aSign ) z = - z; + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the single-precision floating-point format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float64_to_float32( float64 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp; + bits64 aSig; + bits32 zSig; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + if ( aExp == 0x7FF ) { + if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a STATUS_VAR ) ); + return packFloat32( aSign, 0xFF, 0 ); + } + shift64RightJamming( aSig, 22, &aSig ); + zSig = aSig; + if ( aExp || zSig ) { + zSig |= 0x40000000; + aExp -= 0x381; + } + return roundAndPackFloat32( aSign, aExp, zSig STATUS_VAR ); + +} + +#ifdef FLOATX80 + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the extended double-precision floating-point format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 float64_to_floatx80( float64 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp; + bits64 aSig; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + if ( aExp == 0x7FF ) { + if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a STATUS_VAR ) ); + return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 ); + normalizeFloat64Subnormal( aSig, &aExp, &aSig ); + } + return + packFloatx80( + aSign, aExp + 0x3C00, ( aSig | LIT64( 0x0010000000000000 ) )<<11 ); + +} + +#endif + +#ifdef FLOAT128 + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the quadruple-precision floating-point format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float64_to_float128( float64 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp; + bits64 aSig, zSig0, zSig1; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + if ( aExp == 0x7FF ) { + if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a STATUS_VAR ) ); + return packFloat128( aSign, 0x7FFF, 0, 0 ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 ); + normalizeFloat64Subnormal( aSig, &aExp, &aSig ); + --aExp; + } + shift128Right( aSig, 0, 4, &zSig0, &zSig1 ); + return packFloat128( aSign, aExp + 0x3C00, zSig0, zSig1 ); + +} + +#endif + +/*---------------------------------------------------------------------------- +| Rounds the double-precision floating-point value `a' to an integer, and +| returns the result as a double-precision floating-point value. The +| operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_round_to_int( float64 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp; + bits64 lastBitMask, roundBitsMask; + int8 roundingMode; + float64 z; + + aExp = extractFloat64Exp( a ); + if ( 0x433 <= aExp ) { + if ( ( aExp == 0x7FF ) && extractFloat64Frac( a ) ) { + return propagateFloat64NaN( a, a STATUS_VAR ); + } + return a; + } + if ( aExp < 0x3FF ) { + if ( (bits64) ( a<<1 ) == 0 ) return a; + STATUS(float_exception_flags) |= float_flag_inexact; + aSign = extractFloat64Sign( a ); + switch ( STATUS(float_rounding_mode) ) { + case float_round_nearest_even: + if ( ( aExp == 0x3FE ) && extractFloat64Frac( a ) ) { + return packFloat64( aSign, 0x3FF, 0 ); + } + break; + case float_round_down: + return aSign ? LIT64( 0xBFF0000000000000 ) : 0; + case float_round_up: + return + aSign ? LIT64( 0x8000000000000000 ) : LIT64( 0x3FF0000000000000 ); + } + return packFloat64( aSign, 0, 0 ); + } + lastBitMask = 1; + lastBitMask <<= 0x433 - aExp; + roundBitsMask = lastBitMask - 1; + z = a; + roundingMode = STATUS(float_rounding_mode); + if ( roundingMode == float_round_nearest_even ) { + z += lastBitMask>>1; + if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask; + } + else if ( roundingMode != float_round_to_zero ) { + if ( extractFloat64Sign( z ) ^ ( roundingMode == float_round_up ) ) { + z += roundBitsMask; + } + } + z &= ~ roundBitsMask; + if ( z != a ) STATUS(float_exception_flags) |= float_flag_inexact; + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the absolute values of the double-precision +| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated +| before being returned. `zSign' is ignored if the result is a NaN. +| The addition is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) +{ + int16 aExp, bExp, zExp; + bits64 aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + bSig = extractFloat64Frac( b ); + bExp = extractFloat64Exp( b ); + expDiff = aExp - bExp; + aSig <<= 9; + bSig <<= 9; + if ( 0 < expDiff ) { + if ( aExp == 0x7FF ) { + if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + return a; + } + if ( bExp == 0 ) { + --expDiff; + } + else { + bSig |= LIT64( 0x2000000000000000 ); + } + shift64RightJamming( bSig, expDiff, &bSig ); + zExp = aExp; + } + else if ( expDiff < 0 ) { + if ( bExp == 0x7FF ) { + if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + return packFloat64( zSign, 0x7FF, 0 ); + } + if ( aExp == 0 ) { + ++expDiff; + } + else { + aSig |= LIT64( 0x2000000000000000 ); + } + shift64RightJamming( aSig, - expDiff, &aSig ); + zExp = bExp; + } + else { + if ( aExp == 0x7FF ) { + if ( aSig | bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + return a; + } + if ( aExp == 0 ) return packFloat64( zSign, 0, ( aSig + bSig )>>9 ); + zSig = LIT64( 0x4000000000000000 ) + aSig + bSig; + zExp = aExp; + goto roundAndPack; + } + aSig |= LIT64( 0x2000000000000000 ); + zSig = ( aSig + bSig )<<1; + --zExp; + if ( (sbits64) zSig < 0 ) { + zSig = aSig + bSig; + ++zExp; + } + roundAndPack: + return roundAndPackFloat64( zSign, zExp, zSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the absolute values of the double- +| precision floating-point values `a' and `b'. If `zSign' is 1, the +| difference is negated before being returned. `zSign' is ignored if the +| result is a NaN. The subtraction is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM ) +{ + int16 aExp, bExp, zExp; + bits64 aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + bSig = extractFloat64Frac( b ); + bExp = extractFloat64Exp( b ); + expDiff = aExp - bExp; + aSig <<= 10; + bSig <<= 10; + if ( 0 < expDiff ) goto aExpBigger; + if ( expDiff < 0 ) goto bExpBigger; + if ( aExp == 0x7FF ) { + if ( aSig | bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + float_raise( float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + if ( aExp == 0 ) { + aExp = 1; + bExp = 1; + } + if ( bSig < aSig ) goto aBigger; + if ( aSig < bSig ) goto bBigger; + return packFloat64( STATUS(float_rounding_mode) == float_round_down, 0, 0 ); + bExpBigger: + if ( bExp == 0x7FF ) { + if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + return packFloat64( zSign ^ 1, 0x7FF, 0 ); + } + if ( aExp == 0 ) { + ++expDiff; + } + else { + aSig |= LIT64( 0x4000000000000000 ); + } + shift64RightJamming( aSig, - expDiff, &aSig ); + bSig |= LIT64( 0x4000000000000000 ); + bBigger: + zSig = bSig - aSig; + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; + aExpBigger: + if ( aExp == 0x7FF ) { + if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + return a; + } + if ( bExp == 0 ) { + --expDiff; + } + else { + bSig |= LIT64( 0x4000000000000000 ); + } + shift64RightJamming( bSig, expDiff, &bSig ); + aSig |= LIT64( 0x4000000000000000 ); + aBigger: + zSig = aSig - bSig; + zExp = aExp; + normalizeRoundAndPack: + --zExp; + return normalizeRoundAndPackFloat64( zSign, zExp, zSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the double-precision floating-point values `a' +| and `b'. The operation is performed according to the IEC/IEEE Standard for +| Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_add( float64 a, float64 b STATUS_PARAM ) +{ + flag aSign, bSign; + + aSign = extractFloat64Sign( a ); + bSign = extractFloat64Sign( b ); + if ( aSign == bSign ) { + return addFloat64Sigs( a, b, aSign STATUS_VAR ); + } + else { + return subFloat64Sigs( a, b, aSign STATUS_VAR ); + } + +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the double-precision floating-point values +| `a' and `b'. The operation is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_sub( float64 a, float64 b STATUS_PARAM ) +{ + flag aSign, bSign; + + aSign = extractFloat64Sign( a ); + bSign = extractFloat64Sign( b ); + if ( aSign == bSign ) { + return subFloat64Sigs( a, b, aSign STATUS_VAR ); + } + else { + return addFloat64Sigs( a, b, aSign STATUS_VAR ); + } + +} + +/*---------------------------------------------------------------------------- +| Returns the result of multiplying the double-precision floating-point values +| `a' and `b'. The operation is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_mul( float64 a, float64 b STATUS_PARAM ) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + bits64 aSig, bSig, zSig0, zSig1; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + bSig = extractFloat64Frac( b ); + bExp = extractFloat64Exp( b ); + bSign = extractFloat64Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0x7FF ) { + if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) { + return propagateFloat64NaN( a, b STATUS_VAR ); + } + if ( ( bExp | bSig ) == 0 ) { + float_raise( float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + return packFloat64( zSign, 0x7FF, 0 ); + } + if ( bExp == 0x7FF ) { + if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + if ( ( aExp | aSig ) == 0 ) { + float_raise( float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + return packFloat64( zSign, 0x7FF, 0 ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloat64( zSign, 0, 0 ); + normalizeFloat64Subnormal( aSig, &aExp, &aSig ); + } + if ( bExp == 0 ) { + if ( bSig == 0 ) return packFloat64( zSign, 0, 0 ); + normalizeFloat64Subnormal( bSig, &bExp, &bSig ); + } + zExp = aExp + bExp - 0x3FF; + aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10; + bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11; + mul64To128( aSig, bSig, &zSig0, &zSig1 ); + zSig0 |= ( zSig1 != 0 ); + if ( 0 <= (sbits64) ( zSig0<<1 ) ) { + zSig0 <<= 1; + --zExp; + } + return roundAndPackFloat64( zSign, zExp, zSig0 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of dividing the double-precision floating-point value `a' +| by the corresponding value `b'. The operation is performed according to +| the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_div( float64 a, float64 b STATUS_PARAM ) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + bits64 aSig, bSig, zSig; + bits64 rem0, rem1; + bits64 term0, term1; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + bSig = extractFloat64Frac( b ); + bExp = extractFloat64Exp( b ); + bSign = extractFloat64Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0x7FF ) { + if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + if ( bExp == 0x7FF ) { + if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + float_raise( float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + return packFloat64( zSign, 0x7FF, 0 ); + } + if ( bExp == 0x7FF ) { + if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + return packFloat64( zSign, 0, 0 ); + } + if ( bExp == 0 ) { + if ( bSig == 0 ) { + if ( ( aExp | aSig ) == 0 ) { + float_raise( float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + float_raise( float_flag_divbyzero STATUS_VAR); + return packFloat64( zSign, 0x7FF, 0 ); + } + normalizeFloat64Subnormal( bSig, &bExp, &bSig ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloat64( zSign, 0, 0 ); + normalizeFloat64Subnormal( aSig, &aExp, &aSig ); + } + zExp = aExp - bExp + 0x3FD; + aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10; + bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11; + if ( bSig <= ( aSig + aSig ) ) { + aSig >>= 1; + ++zExp; + } + zSig = estimateDiv128To64( aSig, 0, bSig ); + if ( ( zSig & 0x1FF ) <= 2 ) { + mul64To128( bSig, zSig, &term0, &term1 ); + sub128( aSig, 0, term0, term1, &rem0, &rem1 ); + while ( (sbits64) rem0 < 0 ) { + --zSig; + add128( rem0, rem1, 0, bSig, &rem0, &rem1 ); + } + zSig |= ( rem1 != 0 ); + } + return roundAndPackFloat64( zSign, zExp, zSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the remainder of the double-precision floating-point value `a' +| with respect to the corresponding value `b'. The operation is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_rem( float64 a, float64 b STATUS_PARAM ) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, expDiff; + bits64 aSig, bSig; + bits64 q, alternateASig; + sbits64 sigMean; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + bSig = extractFloat64Frac( b ); + bExp = extractFloat64Exp( b ); + bSign = extractFloat64Sign( b ); + if ( aExp == 0x7FF ) { + if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) { + return propagateFloat64NaN( a, b STATUS_VAR ); + } + float_raise( float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + if ( bExp == 0x7FF ) { + if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR ); + return a; + } + if ( bExp == 0 ) { + if ( bSig == 0 ) { + float_raise( float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + normalizeFloat64Subnormal( bSig, &bExp, &bSig ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return a; + normalizeFloat64Subnormal( aSig, &aExp, &aSig ); + } + expDiff = aExp - bExp; + aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<11; + bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11; + if ( expDiff < 0 ) { + if ( expDiff < -1 ) return a; + aSig >>= 1; + } + q = ( bSig <= aSig ); + if ( q ) aSig -= bSig; + expDiff -= 64; + while ( 0 < expDiff ) { + q = estimateDiv128To64( aSig, 0, bSig ); + q = ( 2 < q ) ? q - 2 : 0; + aSig = - ( ( bSig>>2 ) * q ); + expDiff -= 62; + } + expDiff += 64; + if ( 0 < expDiff ) { + q = estimateDiv128To64( aSig, 0, bSig ); + q = ( 2 < q ) ? q - 2 : 0; + q >>= 64 - expDiff; + bSig >>= 2; + aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q; + } + else { + aSig >>= 2; + bSig >>= 2; + } + do { + alternateASig = aSig; + ++q; + aSig -= bSig; + } while ( 0 <= (sbits64) aSig ); + sigMean = aSig + alternateASig; + if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) { + aSig = alternateASig; + } + zSign = ( (sbits64) aSig < 0 ); + if ( zSign ) aSig = - aSig; + return normalizeRoundAndPackFloat64( aSign ^ zSign, bExp, aSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the square root of the double-precision floating-point value `a'. +| The operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_sqrt( float64 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp, zExp; + bits64 aSig, zSig, doubleZSig; + bits64 rem0, rem1, term0, term1; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + if ( aExp == 0x7FF ) { + if ( aSig ) return propagateFloat64NaN( a, a STATUS_VAR ); + if ( ! aSign ) return a; + float_raise( float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + if ( aSign ) { + if ( ( aExp | aSig ) == 0 ) return a; + float_raise( float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return 0; + normalizeFloat64Subnormal( aSig, &aExp, &aSig ); + } + zExp = ( ( aExp - 0x3FF )>>1 ) + 0x3FE; + aSig |= LIT64( 0x0010000000000000 ); + zSig = estimateSqrt32( aExp, aSig>>21 ); + aSig <<= 9 - ( aExp & 1 ); + zSig = estimateDiv128To64( aSig, 0, zSig<<32 ) + ( zSig<<30 ); + if ( ( zSig & 0x1FF ) <= 5 ) { + doubleZSig = zSig<<1; + mul64To128( zSig, zSig, &term0, &term1 ); + sub128( aSig, 0, term0, term1, &rem0, &rem1 ); + while ( (sbits64) rem0 < 0 ) { + --zSig; + doubleZSig -= 2; + add128( rem0, rem1, zSig>>63, doubleZSig | 1, &rem0, &rem1 ); + } + zSig |= ( ( rem0 | rem1 ) != 0 ); + } + return roundAndPackFloat64( 0, zExp, zSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is equal to the +| corresponding value `b', and 0 otherwise. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float64_eq( float64 a, float64 b STATUS_PARAM ) +{ + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + return ( a == b ) || ( (bits64) ( ( a | b )<<1 ) == 0 ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is less than or +| equal to the corresponding value `b', and 0 otherwise. The comparison is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float64_le( float64 a, float64 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloat64Sign( a ); + bSign = extractFloat64Sign( b ); + if ( aSign != bSign ) return aSign || ( (bits64) ( ( a | b )<<1 ) == 0 ); + return ( a == b ) || ( aSign ^ ( a < b ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is less than +| the corresponding value `b', and 0 otherwise. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float64_lt( float64 a, float64 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloat64Sign( a ); + bSign = extractFloat64Sign( b ); + if ( aSign != bSign ) return aSign && ( (bits64) ( ( a | b )<<1 ) != 0 ); + return ( a != b ) && ( aSign ^ ( a < b ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is equal to the +| corresponding value `b', and 0 otherwise. The invalid exception is raised +| if either operand is a NaN. Otherwise, the comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float64_eq_signaling( float64 a, float64 b STATUS_PARAM ) +{ + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 0; + } + return ( a == b ) || ( (bits64) ( ( a | b )<<1 ) == 0 ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is less than or +| equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not +| cause an exception. Otherwise, the comparison is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float64_le_quiet( float64 a, float64 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloat64Sign( a ); + bSign = extractFloat64Sign( b ); + if ( aSign != bSign ) return aSign || ( (bits64) ( ( a | b )<<1 ) == 0 ); + return ( a == b ) || ( aSign ^ ( a < b ) ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is less than +| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception. Otherwise, the comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float64_lt_quiet( float64 a, float64 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloat64Sign( a ); + bSign = extractFloat64Sign( b ); + if ( aSign != bSign ) return aSign && ( (bits64) ( ( a | b )<<1 ) != 0 ); + return ( a != b ) && ( aSign ^ ( a < b ) ); + +} + +#ifdef FLOATX80 + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point value `a' to the 32-bit two's complement integer format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic---which means in particular that the conversion +| is rounded according to the current rounding mode. If `a' is a NaN, the +| largest positive integer is returned. Otherwise, if the conversion +| overflows, the largest integer with the same sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int32 floatx80_to_int32( floatx80 a STATUS_PARAM ) +{ + flag aSign; + int32 aExp, shiftCount; + bits64 aSig; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0; + shiftCount = 0x4037 - aExp; + if ( shiftCount <= 0 ) shiftCount = 1; + shift64RightJamming( aSig, shiftCount, &aSig ); + return roundAndPackInt32( aSign, aSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point value `a' to the 32-bit two's complement integer format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic, except that the conversion is always rounded +| toward zero. If `a' is a NaN, the largest positive integer is returned. +| Otherwise, if the conversion overflows, the largest integer with the same +| sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int32 floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM ) +{ + flag aSign; + int32 aExp, shiftCount; + bits64 aSig, savedASig; + int32 z; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + if ( 0x401E < aExp ) { + if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0; + goto invalid; + } + else if ( aExp < 0x3FFF ) { + if ( aExp || aSig ) STATUS(float_exception_flags) |= float_flag_inexact; + return 0; + } + shiftCount = 0x403E - aExp; + savedASig = aSig; + aSig >>= shiftCount; + z = aSig; + if ( aSign ) z = - z; + if ( ( z < 0 ) ^ aSign ) { + invalid: + float_raise( float_flag_invalid STATUS_VAR); + return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF; + } + if ( ( aSig<>( - shiftCount ); + if ( (bits64) ( aSig<<( shiftCount & 63 ) ) ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + if ( aSign ) z = - z; + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point value `a' to the single-precision floating-point format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 floatx80_to_float32( floatx80 a STATUS_PARAM ) +{ + flag aSign; + int32 aExp; + bits64 aSig; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig<<1 ) ) { + return commonNaNToFloat32( floatx80ToCommonNaN( a STATUS_VAR ) ); + } + return packFloat32( aSign, 0xFF, 0 ); + } + shift64RightJamming( aSig, 33, &aSig ); + if ( aExp || aSig ) aExp -= 0x3F81; + return roundAndPackFloat32( aSign, aExp, aSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point value `a' to the double-precision floating-point format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 floatx80_to_float64( floatx80 a STATUS_PARAM ) +{ + flag aSign; + int32 aExp; + bits64 aSig, zSig; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig<<1 ) ) { + return commonNaNToFloat64( floatx80ToCommonNaN( a STATUS_VAR ) ); + } + return packFloat64( aSign, 0x7FF, 0 ); + } + shift64RightJamming( aSig, 1, &zSig ); + if ( aExp || aSig ) aExp -= 0x3C01; + return roundAndPackFloat64( aSign, aExp, zSig STATUS_VAR ); + +} + +#ifdef FLOAT128 + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point value `a' to the quadruple-precision floating-point format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 floatx80_to_float128( floatx80 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp; + bits64 aSig, zSig0, zSig1; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) { + return commonNaNToFloat128( floatx80ToCommonNaN( a STATUS_VAR ) ); + } + shift128Right( aSig<<1, 0, 16, &zSig0, &zSig1 ); + return packFloat128( aSign, aExp, zSig0, zSig1 ); + +} + +#endif + +/*---------------------------------------------------------------------------- +| Rounds the extended double-precision floating-point value `a' to an integer, +| and returns the result as an extended quadruple-precision floating-point +| value. The operation is performed according to the IEC/IEEE Standard for +| Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM ) +{ + flag aSign; + int32 aExp; + bits64 lastBitMask, roundBitsMask; + int8 roundingMode; + floatx80 z; + + aExp = extractFloatx80Exp( a ); + if ( 0x403E <= aExp ) { + if ( ( aExp == 0x7FFF ) && (bits64) ( extractFloatx80Frac( a )<<1 ) ) { + return propagateFloatx80NaN( a, a STATUS_VAR ); + } + return a; + } + if ( aExp < 0x3FFF ) { + if ( ( aExp == 0 ) + && ( (bits64) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) { + return a; + } + STATUS(float_exception_flags) |= float_flag_inexact; + aSign = extractFloatx80Sign( a ); + switch ( STATUS(float_rounding_mode) ) { + case float_round_nearest_even: + if ( ( aExp == 0x3FFE ) && (bits64) ( extractFloatx80Frac( a )<<1 ) + ) { + return + packFloatx80( aSign, 0x3FFF, LIT64( 0x8000000000000000 ) ); + } + break; + case float_round_down: + return + aSign ? + packFloatx80( 1, 0x3FFF, LIT64( 0x8000000000000000 ) ) + : packFloatx80( 0, 0, 0 ); + case float_round_up: + return + aSign ? packFloatx80( 1, 0, 0 ) + : packFloatx80( 0, 0x3FFF, LIT64( 0x8000000000000000 ) ); + } + return packFloatx80( aSign, 0, 0 ); + } + lastBitMask = 1; + lastBitMask <<= 0x403E - aExp; + roundBitsMask = lastBitMask - 1; + z = a; + roundingMode = STATUS(float_rounding_mode); + if ( roundingMode == float_round_nearest_even ) { + z.low += lastBitMask>>1; + if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask; + } + else if ( roundingMode != float_round_to_zero ) { + if ( extractFloatx80Sign( z ) ^ ( roundingMode == float_round_up ) ) { + z.low += roundBitsMask; + } + } + z.low &= ~ roundBitsMask; + if ( z.low == 0 ) { + ++z.high; + z.low = LIT64( 0x8000000000000000 ); + } + if ( z.low != a.low ) STATUS(float_exception_flags) |= float_flag_inexact; + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the absolute values of the extended double- +| precision floating-point values `a' and `b'. If `zSign' is 1, the sum is +| negated before being returned. `zSign' is ignored if the result is a NaN. +| The addition is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM) +{ + int32 aExp, bExp, zExp; + bits64 aSig, bSig, zSig0, zSig1; + int32 expDiff; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + bSig = extractFloatx80Frac( b ); + bExp = extractFloatx80Exp( b ); + expDiff = aExp - bExp; + if ( 0 < expDiff ) { + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + return a; + } + if ( bExp == 0 ) --expDiff; + shift64ExtraRightJamming( bSig, 0, expDiff, &bSig, &zSig1 ); + zExp = aExp; + } + else if ( expDiff < 0 ) { + if ( bExp == 0x7FFF ) { + if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( aExp == 0 ) ++expDiff; + shift64ExtraRightJamming( aSig, 0, - expDiff, &aSig, &zSig1 ); + zExp = bExp; + } + else { + if ( aExp == 0x7FFF ) { + if ( (bits64) ( ( aSig | bSig )<<1 ) ) { + return propagateFloatx80NaN( a, b STATUS_VAR ); + } + return a; + } + zSig1 = 0; + zSig0 = aSig + bSig; + if ( aExp == 0 ) { + normalizeFloatx80Subnormal( zSig0, &zExp, &zSig0 ); + goto roundAndPack; + } + zExp = aExp; + goto shiftRight1; + } + zSig0 = aSig + bSig; + if ( (sbits64) zSig0 < 0 ) goto roundAndPack; + shiftRight1: + shift64ExtraRightJamming( zSig0, zSig1, 1, &zSig0, &zSig1 ); + zSig0 |= LIT64( 0x8000000000000000 ); + ++zExp; + roundAndPack: + return + roundAndPackFloatx80( + STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the absolute values of the extended +| double-precision floating-point values `a' and `b'. If `zSign' is 1, the +| difference is negated before being returned. `zSign' is ignored if the +| result is a NaN. The subtraction is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM ) +{ + int32 aExp, bExp, zExp; + bits64 aSig, bSig, zSig0, zSig1; + int32 expDiff; + floatx80 z; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + bSig = extractFloatx80Frac( b ); + bExp = extractFloatx80Exp( b ); + expDiff = aExp - bExp; + if ( 0 < expDiff ) goto aExpBigger; + if ( expDiff < 0 ) goto bExpBigger; + if ( aExp == 0x7FFF ) { + if ( (bits64) ( ( aSig | bSig )<<1 ) ) { + return propagateFloatx80NaN( a, b STATUS_VAR ); + } + float_raise( float_flag_invalid STATUS_VAR); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + if ( aExp == 0 ) { + aExp = 1; + bExp = 1; + } + zSig1 = 0; + if ( bSig < aSig ) goto aBigger; + if ( aSig < bSig ) goto bBigger; + return packFloatx80( STATUS(float_rounding_mode) == float_round_down, 0, 0 ); + bExpBigger: + if ( bExp == 0x7FFF ) { + if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + return packFloatx80( zSign ^ 1, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( aExp == 0 ) ++expDiff; + shift128RightJamming( aSig, 0, - expDiff, &aSig, &zSig1 ); + bBigger: + sub128( bSig, 0, aSig, zSig1, &zSig0, &zSig1 ); + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; + aExpBigger: + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + return a; + } + if ( bExp == 0 ) --expDiff; + shift128RightJamming( bSig, 0, expDiff, &bSig, &zSig1 ); + aBigger: + sub128( aSig, 0, bSig, zSig1, &zSig0, &zSig1 ); + zExp = aExp; + normalizeRoundAndPack: + return + normalizeRoundAndPackFloatx80( + STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the extended double-precision floating-point +| values `a' and `b'. The operation is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_add( floatx80 a, floatx80 b STATUS_PARAM ) +{ + flag aSign, bSign; + + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign == bSign ) { + return addFloatx80Sigs( a, b, aSign STATUS_VAR ); + } + else { + return subFloatx80Sigs( a, b, aSign STATUS_VAR ); + } + +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the extended double-precision floating- +| point values `a' and `b'. The operation is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_sub( floatx80 a, floatx80 b STATUS_PARAM ) +{ + flag aSign, bSign; + + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign == bSign ) { + return subFloatx80Sigs( a, b, aSign STATUS_VAR ); + } + else { + return addFloatx80Sigs( a, b, aSign STATUS_VAR ); + } + +} + +/*---------------------------------------------------------------------------- +| Returns the result of multiplying the extended double-precision floating- +| point values `a' and `b'. The operation is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM ) +{ + flag aSign, bSign, zSign; + int32 aExp, bExp, zExp; + bits64 aSig, bSig, zSig0, zSig1; + floatx80 z; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + bSig = extractFloatx80Frac( b ); + bExp = extractFloatx80Exp( b ); + bSign = extractFloatx80Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig<<1 ) + || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) { + return propagateFloatx80NaN( a, b STATUS_VAR ); + } + if ( ( bExp | bSig ) == 0 ) goto invalid; + return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( bExp == 0x7FFF ) { + if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( ( aExp | aSig ) == 0 ) { + invalid: + float_raise( float_flag_invalid STATUS_VAR); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 ); + normalizeFloatx80Subnormal( aSig, &aExp, &aSig ); + } + if ( bExp == 0 ) { + if ( bSig == 0 ) return packFloatx80( zSign, 0, 0 ); + normalizeFloatx80Subnormal( bSig, &bExp, &bSig ); + } + zExp = aExp + bExp - 0x3FFE; + mul64To128( aSig, bSig, &zSig0, &zSig1 ); + if ( 0 < (sbits64) zSig0 ) { + shortShift128Left( zSig0, zSig1, 1, &zSig0, &zSig1 ); + --zExp; + } + return + roundAndPackFloatx80( + STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of dividing the extended double-precision floating-point +| value `a' by the corresponding value `b'. The operation is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM ) +{ + flag aSign, bSign, zSign; + int32 aExp, bExp, zExp; + bits64 aSig, bSig, zSig0, zSig1; + bits64 rem0, rem1, rem2, term0, term1, term2; + floatx80 z; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + bSig = extractFloatx80Frac( b ); + bExp = extractFloatx80Exp( b ); + bSign = extractFloatx80Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + if ( bExp == 0x7FFF ) { + if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + goto invalid; + } + return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( bExp == 0x7FFF ) { + if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + return packFloatx80( zSign, 0, 0 ); + } + if ( bExp == 0 ) { + if ( bSig == 0 ) { + if ( ( aExp | aSig ) == 0 ) { + invalid: + float_raise( float_flag_invalid STATUS_VAR); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + float_raise( float_flag_divbyzero STATUS_VAR); + return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + normalizeFloatx80Subnormal( bSig, &bExp, &bSig ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 ); + normalizeFloatx80Subnormal( aSig, &aExp, &aSig ); + } + zExp = aExp - bExp + 0x3FFE; + rem1 = 0; + if ( bSig <= aSig ) { + shift128Right( aSig, 0, 1, &aSig, &rem1 ); + ++zExp; + } + zSig0 = estimateDiv128To64( aSig, rem1, bSig ); + mul64To128( bSig, zSig0, &term0, &term1 ); + sub128( aSig, rem1, term0, term1, &rem0, &rem1 ); + while ( (sbits64) rem0 < 0 ) { + --zSig0; + add128( rem0, rem1, 0, bSig, &rem0, &rem1 ); + } + zSig1 = estimateDiv128To64( rem1, 0, bSig ); + if ( (bits64) ( zSig1<<1 ) <= 8 ) { + mul64To128( bSig, zSig1, &term1, &term2 ); + sub128( rem1, 0, term1, term2, &rem1, &rem2 ); + while ( (sbits64) rem1 < 0 ) { + --zSig1; + add128( rem1, rem2, 0, bSig, &rem1, &rem2 ); + } + zSig1 |= ( ( rem1 | rem2 ) != 0 ); + } + return + roundAndPackFloatx80( + STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the remainder of the extended double-precision floating-point value +| `a' with respect to the corresponding value `b'. The operation is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM ) +{ + flag aSign, bSign, zSign; + int32 aExp, bExp, expDiff; + bits64 aSig0, aSig1, bSig; + bits64 q, term0, term1, alternateASig0, alternateASig1; + floatx80 z; + + aSig0 = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + bSig = extractFloatx80Frac( b ); + bExp = extractFloatx80Exp( b ); + bSign = extractFloatx80Sign( b ); + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig0<<1 ) + || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) { + return propagateFloatx80NaN( a, b STATUS_VAR ); + } + goto invalid; + } + if ( bExp == 0x7FFF ) { + if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR ); + return a; + } + if ( bExp == 0 ) { + if ( bSig == 0 ) { + invalid: + float_raise( float_flag_invalid STATUS_VAR); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + normalizeFloatx80Subnormal( bSig, &bExp, &bSig ); + } + if ( aExp == 0 ) { + if ( (bits64) ( aSig0<<1 ) == 0 ) return a; + normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 ); + } + bSig |= LIT64( 0x8000000000000000 ); + zSign = aSign; + expDiff = aExp - bExp; + aSig1 = 0; + if ( expDiff < 0 ) { + if ( expDiff < -1 ) return a; + shift128Right( aSig0, 0, 1, &aSig0, &aSig1 ); + expDiff = 0; + } + q = ( bSig <= aSig0 ); + if ( q ) aSig0 -= bSig; + expDiff -= 64; + while ( 0 < expDiff ) { + q = estimateDiv128To64( aSig0, aSig1, bSig ); + q = ( 2 < q ) ? q - 2 : 0; + mul64To128( bSig, q, &term0, &term1 ); + sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 ); + shortShift128Left( aSig0, aSig1, 62, &aSig0, &aSig1 ); + expDiff -= 62; + } + expDiff += 64; + if ( 0 < expDiff ) { + q = estimateDiv128To64( aSig0, aSig1, bSig ); + q = ( 2 < q ) ? q - 2 : 0; + q >>= 64 - expDiff; + mul64To128( bSig, q<<( 64 - expDiff ), &term0, &term1 ); + sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 ); + shortShift128Left( 0, bSig, 64 - expDiff, &term0, &term1 ); + while ( le128( term0, term1, aSig0, aSig1 ) ) { + ++q; + sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 ); + } + } + else { + term1 = 0; + term0 = bSig; + } + sub128( term0, term1, aSig0, aSig1, &alternateASig0, &alternateASig1 ); + if ( lt128( alternateASig0, alternateASig1, aSig0, aSig1 ) + || ( eq128( alternateASig0, alternateASig1, aSig0, aSig1 ) + && ( q & 1 ) ) + ) { + aSig0 = alternateASig0; + aSig1 = alternateASig1; + zSign = ! zSign; + } + return + normalizeRoundAndPackFloatx80( + 80, zSign, bExp + expDiff, aSig0, aSig1 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the square root of the extended double-precision floating-point +| value `a'. The operation is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM ) +{ + flag aSign; + int32 aExp, zExp; + bits64 aSig0, aSig1, zSig0, zSig1, doubleZSig0; + bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; + floatx80 z; + + aSig0 = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig0<<1 ) ) return propagateFloatx80NaN( a, a STATUS_VAR ); + if ( ! aSign ) return a; + goto invalid; + } + if ( aSign ) { + if ( ( aExp | aSig0 ) == 0 ) return a; + invalid: + float_raise( float_flag_invalid STATUS_VAR); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + if ( aExp == 0 ) { + if ( aSig0 == 0 ) return packFloatx80( 0, 0, 0 ); + normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 ); + } + zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFF; + zSig0 = estimateSqrt32( aExp, aSig0>>32 ); + shift128Right( aSig0, 0, 2 + ( aExp & 1 ), &aSig0, &aSig1 ); + zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0<<32 ) + ( zSig0<<30 ); + doubleZSig0 = zSig0<<1; + mul64To128( zSig0, zSig0, &term0, &term1 ); + sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 ); + while ( (sbits64) rem0 < 0 ) { + --zSig0; + doubleZSig0 -= 2; + add128( rem0, rem1, zSig0>>63, doubleZSig0 | 1, &rem0, &rem1 ); + } + zSig1 = estimateDiv128To64( rem1, 0, doubleZSig0 ); + if ( ( zSig1 & LIT64( 0x3FFFFFFFFFFFFFFF ) ) <= 5 ) { + if ( zSig1 == 0 ) zSig1 = 1; + mul64To128( doubleZSig0, zSig1, &term1, &term2 ); + sub128( rem1, 0, term1, term2, &rem1, &rem2 ); + mul64To128( zSig1, zSig1, &term2, &term3 ); + sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 ); + while ( (sbits64) rem1 < 0 ) { + --zSig1; + shortShift128Left( 0, zSig1, 1, &term2, &term3 ); + term3 |= 1; + term2 |= doubleZSig0; + add192( rem1, rem2, rem3, 0, term2, term3, &rem1, &rem2, &rem3 ); + } + zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 ); + } + shortShift128Left( 0, zSig1, 1, &zSig0, &zSig1 ); + zSig0 |= doubleZSig0; + return + roundAndPackFloatx80( + STATUS(floatx80_rounding_precision), 0, zExp, zSig0, zSig1 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is +| equal to the corresponding value `b', and 0 otherwise. The comparison is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +flag floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM ) +{ + + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + ) { + if ( floatx80_is_signaling_nan( a ) + || floatx80_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + return + ( a.low == b.low ) + && ( ( a.high == b.high ) + || ( ( a.low == 0 ) + && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) ) + ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is +| less than or equal to the corresponding value `b', and 0 otherwise. The +| comparison is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag floatx80_le( floatx80 a, floatx80 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign != bSign ) { + return + aSign + || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + == 0 ); + } + return + aSign ? le128( b.high, b.low, a.high, a.low ) + : le128( a.high, a.low, b.high, b.low ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is +| less than the corresponding value `b', and 0 otherwise. The comparison +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +flag floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign != bSign ) { + return + aSign + && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + != 0 ); + } + return + aSign ? lt128( b.high, b.low, a.high, a.low ) + : lt128( a.high, a.low, b.high, b.low ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is equal +| to the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. Otherwise, the comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM ) +{ + + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 0; + } + return + ( a.low == b.low ) + && ( ( a.high == b.high ) + || ( ( a.low == 0 ) + && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) ) + ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is less +| than or equal to the corresponding value `b', and 0 otherwise. Quiet NaNs +| do not cause an exception. Otherwise, the comparison is performed according +| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + ) { + if ( floatx80_is_signaling_nan( a ) + || floatx80_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign != bSign ) { + return + aSign + || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + == 0 ); + } + return + aSign ? le128( b.high, b.low, a.high, a.low ) + : le128( a.high, a.low, b.high, b.low ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is less +| than the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause +| an exception. Otherwise, the comparison is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + ) { + if ( floatx80_is_signaling_nan( a ) + || floatx80_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign != bSign ) { + return + aSign + && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + != 0 ); + } + return + aSign ? lt128( b.high, b.low, a.high, a.low ) + : lt128( a.high, a.low, b.high, b.low ); + +} + +#endif + +#ifdef FLOAT128 + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point +| value `a' to the 32-bit two's complement integer format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic---which means in particular that the conversion is rounded +| according to the current rounding mode. If `a' is a NaN, the largest +| positive integer is returned. Otherwise, if the conversion overflows, the +| largest integer with the same sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int32 float128_to_int32( float128 a STATUS_PARAM ) +{ + flag aSign; + int32 aExp, shiftCount; + bits64 aSig0, aSig1; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + if ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) aSign = 0; + if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 ); + aSig0 |= ( aSig1 != 0 ); + shiftCount = 0x4028 - aExp; + if ( 0 < shiftCount ) shift64RightJamming( aSig0, shiftCount, &aSig0 ); + return roundAndPackInt32( aSign, aSig0 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point +| value `a' to the 32-bit two's complement integer format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. If +| `a' is a NaN, the largest positive integer is returned. Otherwise, if the +| conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int32 float128_to_int32_round_to_zero( float128 a STATUS_PARAM ) +{ + flag aSign; + int32 aExp, shiftCount; + bits64 aSig0, aSig1, savedASig; + int32 z; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + aSig0 |= ( aSig1 != 0 ); + if ( 0x401E < aExp ) { + if ( ( aExp == 0x7FFF ) && aSig0 ) aSign = 0; + goto invalid; + } + else if ( aExp < 0x3FFF ) { + if ( aExp || aSig0 ) STATUS(float_exception_flags) |= float_flag_inexact; + return 0; + } + aSig0 |= LIT64( 0x0001000000000000 ); + shiftCount = 0x402F - aExp; + savedASig = aSig0; + aSig0 >>= shiftCount; + z = aSig0; + if ( aSign ) z = - z; + if ( ( z < 0 ) ^ aSign ) { + invalid: + float_raise( float_flag_invalid STATUS_VAR); + return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF; + } + if ( ( aSig0<>( ( - shiftCount ) & 63 ) ); + if ( (bits64) ( aSig1<>( - shiftCount ); + if ( aSig1 + || ( shiftCount && (bits64) ( aSig0<<( shiftCount & 63 ) ) ) ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + } + if ( aSign ) z = - z; + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point +| value `a' to the single-precision floating-point format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float128_to_float32( float128 a STATUS_PARAM ) +{ + flag aSign; + int32 aExp; + bits64 aSig0, aSig1; + bits32 zSig; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 ) { + return commonNaNToFloat32( float128ToCommonNaN( a STATUS_VAR ) ); + } + return packFloat32( aSign, 0xFF, 0 ); + } + aSig0 |= ( aSig1 != 0 ); + shift64RightJamming( aSig0, 18, &aSig0 ); + zSig = aSig0; + if ( aExp || zSig ) { + zSig |= 0x40000000; + aExp -= 0x3F81; + } + return roundAndPackFloat32( aSign, aExp, zSig STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point +| value `a' to the double-precision floating-point format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float128_to_float64( float128 a STATUS_PARAM ) +{ + flag aSign; + int32 aExp; + bits64 aSig0, aSig1; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 ) { + return commonNaNToFloat64( float128ToCommonNaN( a STATUS_VAR ) ); + } + return packFloat64( aSign, 0x7FF, 0 ); + } + shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 ); + aSig0 |= ( aSig1 != 0 ); + if ( aExp || aSig0 ) { + aSig0 |= LIT64( 0x4000000000000000 ); + aExp -= 0x3C01; + } + return roundAndPackFloat64( aSign, aExp, aSig0 STATUS_VAR ); + +} + +#ifdef FLOATX80 + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point +| value `a' to the extended double-precision floating-point format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 float128_to_floatx80( float128 a STATUS_PARAM ) +{ + flag aSign; + int32 aExp; + bits64 aSig0, aSig1; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 ) { + return commonNaNToFloatx80( float128ToCommonNaN( a STATUS_VAR ) ); + } + return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( aExp == 0 ) { + if ( ( aSig0 | aSig1 ) == 0 ) return packFloatx80( aSign, 0, 0 ); + normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); + } + else { + aSig0 |= LIT64( 0x0001000000000000 ); + } + shortShift128Left( aSig0, aSig1, 15, &aSig0, &aSig1 ); + return roundAndPackFloatx80( 80, aSign, aExp, aSig0, aSig1 STATUS_VAR ); + +} + +#endif + +/*---------------------------------------------------------------------------- +| Rounds the quadruple-precision floating-point value `a' to an integer, and +| returns the result as a quadruple-precision floating-point value. The +| operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_round_to_int( float128 a STATUS_PARAM ) +{ + flag aSign; + int32 aExp; + bits64 lastBitMask, roundBitsMask; + int8 roundingMode; + float128 z; + + aExp = extractFloat128Exp( a ); + if ( 0x402F <= aExp ) { + if ( 0x406F <= aExp ) { + if ( ( aExp == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) + ) { + return propagateFloat128NaN( a, a STATUS_VAR ); + } + return a; + } + lastBitMask = 1; + lastBitMask = ( lastBitMask<<( 0x406E - aExp ) )<<1; + roundBitsMask = lastBitMask - 1; + z = a; + roundingMode = STATUS(float_rounding_mode); + if ( roundingMode == float_round_nearest_even ) { + if ( lastBitMask ) { + add128( z.high, z.low, 0, lastBitMask>>1, &z.high, &z.low ); + if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask; + } + else { + if ( (sbits64) z.low < 0 ) { + ++z.high; + if ( (bits64) ( z.low<<1 ) == 0 ) z.high &= ~1; + } + } + } + else if ( roundingMode != float_round_to_zero ) { + if ( extractFloat128Sign( z ) + ^ ( roundingMode == float_round_up ) ) { + add128( z.high, z.low, 0, roundBitsMask, &z.high, &z.low ); + } + } + z.low &= ~ roundBitsMask; + } + else { + if ( aExp < 0x3FFF ) { + if ( ( ( (bits64) ( a.high<<1 ) ) | a.low ) == 0 ) return a; + STATUS(float_exception_flags) |= float_flag_inexact; + aSign = extractFloat128Sign( a ); + switch ( STATUS(float_rounding_mode) ) { + case float_round_nearest_even: + if ( ( aExp == 0x3FFE ) + && ( extractFloat128Frac0( a ) + | extractFloat128Frac1( a ) ) + ) { + return packFloat128( aSign, 0x3FFF, 0, 0 ); + } + break; + case float_round_down: + return + aSign ? packFloat128( 1, 0x3FFF, 0, 0 ) + : packFloat128( 0, 0, 0, 0 ); + case float_round_up: + return + aSign ? packFloat128( 1, 0, 0, 0 ) + : packFloat128( 0, 0x3FFF, 0, 0 ); + } + return packFloat128( aSign, 0, 0, 0 ); + } + lastBitMask = 1; + lastBitMask <<= 0x402F - aExp; + roundBitsMask = lastBitMask - 1; + z.low = 0; + z.high = a.high; + roundingMode = STATUS(float_rounding_mode); + if ( roundingMode == float_round_nearest_even ) { + z.high += lastBitMask>>1; + if ( ( ( z.high & roundBitsMask ) | a.low ) == 0 ) { + z.high &= ~ lastBitMask; + } + } + else if ( roundingMode != float_round_to_zero ) { + if ( extractFloat128Sign( z ) + ^ ( roundingMode == float_round_up ) ) { + z.high |= ( a.low != 0 ); + z.high += roundBitsMask; + } + } + z.high &= ~ roundBitsMask; + } + if ( ( z.low != a.low ) || ( z.high != a.high ) ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the absolute values of the quadruple-precision +| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated +| before being returned. `zSign' is ignored if the result is a NaN. +| The addition is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM) +{ + int32 aExp, bExp, zExp; + bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; + int32 expDiff; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + bSig1 = extractFloat128Frac1( b ); + bSig0 = extractFloat128Frac0( b ); + bExp = extractFloat128Exp( b ); + expDiff = aExp - bExp; + if ( 0 < expDiff ) { + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + return a; + } + if ( bExp == 0 ) { + --expDiff; + } + else { + bSig0 |= LIT64( 0x0001000000000000 ); + } + shift128ExtraRightJamming( + bSig0, bSig1, 0, expDiff, &bSig0, &bSig1, &zSig2 ); + zExp = aExp; + } + else if ( expDiff < 0 ) { + if ( bExp == 0x7FFF ) { + if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + return packFloat128( zSign, 0x7FFF, 0, 0 ); + } + if ( aExp == 0 ) { + ++expDiff; + } + else { + aSig0 |= LIT64( 0x0001000000000000 ); + } + shift128ExtraRightJamming( + aSig0, aSig1, 0, - expDiff, &aSig0, &aSig1, &zSig2 ); + zExp = bExp; + } + else { + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 | bSig0 | bSig1 ) { + return propagateFloat128NaN( a, b STATUS_VAR ); + } + return a; + } + add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); + if ( aExp == 0 ) return packFloat128( zSign, 0, zSig0, zSig1 ); + zSig2 = 0; + zSig0 |= LIT64( 0x0002000000000000 ); + zExp = aExp; + goto shiftRight1; + } + aSig0 |= LIT64( 0x0001000000000000 ); + add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); + --zExp; + if ( zSig0 < LIT64( 0x0002000000000000 ) ) goto roundAndPack; + ++zExp; + shiftRight1: + shift128ExtraRightJamming( + zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 ); + roundAndPack: + return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the absolute values of the quadruple- +| precision floating-point values `a' and `b'. If `zSign' is 1, the +| difference is negated before being returned. `zSign' is ignored if the +| result is a NaN. The subtraction is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float128 subFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM) +{ + int32 aExp, bExp, zExp; + bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1; + int32 expDiff; + float128 z; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + bSig1 = extractFloat128Frac1( b ); + bSig0 = extractFloat128Frac0( b ); + bExp = extractFloat128Exp( b ); + expDiff = aExp - bExp; + shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 ); + shortShift128Left( bSig0, bSig1, 14, &bSig0, &bSig1 ); + if ( 0 < expDiff ) goto aExpBigger; + if ( expDiff < 0 ) goto bExpBigger; + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 | bSig0 | bSig1 ) { + return propagateFloat128NaN( a, b STATUS_VAR ); + } + float_raise( float_flag_invalid STATUS_VAR); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + if ( aExp == 0 ) { + aExp = 1; + bExp = 1; + } + if ( bSig0 < aSig0 ) goto aBigger; + if ( aSig0 < bSig0 ) goto bBigger; + if ( bSig1 < aSig1 ) goto aBigger; + if ( aSig1 < bSig1 ) goto bBigger; + return packFloat128( STATUS(float_rounding_mode) == float_round_down, 0, 0, 0 ); + bExpBigger: + if ( bExp == 0x7FFF ) { + if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + return packFloat128( zSign ^ 1, 0x7FFF, 0, 0 ); + } + if ( aExp == 0 ) { + ++expDiff; + } + else { + aSig0 |= LIT64( 0x4000000000000000 ); + } + shift128RightJamming( aSig0, aSig1, - expDiff, &aSig0, &aSig1 ); + bSig0 |= LIT64( 0x4000000000000000 ); + bBigger: + sub128( bSig0, bSig1, aSig0, aSig1, &zSig0, &zSig1 ); + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; + aExpBigger: + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + return a; + } + if ( bExp == 0 ) { + --expDiff; + } + else { + bSig0 |= LIT64( 0x4000000000000000 ); + } + shift128RightJamming( bSig0, bSig1, expDiff, &bSig0, &bSig1 ); + aSig0 |= LIT64( 0x4000000000000000 ); + aBigger: + sub128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); + zExp = aExp; + normalizeRoundAndPack: + --zExp; + return normalizeRoundAndPackFloat128( zSign, zExp - 14, zSig0, zSig1 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the quadruple-precision floating-point values +| `a' and `b'. The operation is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_add( float128 a, float128 b STATUS_PARAM ) +{ + flag aSign, bSign; + + aSign = extractFloat128Sign( a ); + bSign = extractFloat128Sign( b ); + if ( aSign == bSign ) { + return addFloat128Sigs( a, b, aSign STATUS_VAR ); + } + else { + return subFloat128Sigs( a, b, aSign STATUS_VAR ); + } + +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the quadruple-precision floating-point +| values `a' and `b'. The operation is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_sub( float128 a, float128 b STATUS_PARAM ) +{ + flag aSign, bSign; + + aSign = extractFloat128Sign( a ); + bSign = extractFloat128Sign( b ); + if ( aSign == bSign ) { + return subFloat128Sigs( a, b, aSign STATUS_VAR ); + } + else { + return addFloat128Sigs( a, b, aSign STATUS_VAR ); + } + +} + +/*---------------------------------------------------------------------------- +| Returns the result of multiplying the quadruple-precision floating-point +| values `a' and `b'. The operation is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_mul( float128 a, float128 b STATUS_PARAM ) +{ + flag aSign, bSign, zSign; + int32 aExp, bExp, zExp; + bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3; + float128 z; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + bSig1 = extractFloat128Frac1( b ); + bSig0 = extractFloat128Frac0( b ); + bExp = extractFloat128Exp( b ); + bSign = extractFloat128Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0x7FFF ) { + if ( ( aSig0 | aSig1 ) + || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) { + return propagateFloat128NaN( a, b STATUS_VAR ); + } + if ( ( bExp | bSig0 | bSig1 ) == 0 ) goto invalid; + return packFloat128( zSign, 0x7FFF, 0, 0 ); + } + if ( bExp == 0x7FFF ) { + if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + if ( ( aExp | aSig0 | aSig1 ) == 0 ) { + invalid: + float_raise( float_flag_invalid STATUS_VAR); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + return packFloat128( zSign, 0x7FFF, 0, 0 ); + } + if ( aExp == 0 ) { + if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 ); + normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); + } + if ( bExp == 0 ) { + if ( ( bSig0 | bSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 ); + normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 ); + } + zExp = aExp + bExp - 0x4000; + aSig0 |= LIT64( 0x0001000000000000 ); + shortShift128Left( bSig0, bSig1, 16, &bSig0, &bSig1 ); + mul128To256( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1, &zSig2, &zSig3 ); + add128( zSig0, zSig1, aSig0, aSig1, &zSig0, &zSig1 ); + zSig2 |= ( zSig3 != 0 ); + if ( LIT64( 0x0002000000000000 ) <= zSig0 ) { + shift128ExtraRightJamming( + zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 ); + ++zExp; + } + return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the result of dividing the quadruple-precision floating-point value +| `a' by the corresponding value `b'. The operation is performed according to +| the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_div( float128 a, float128 b STATUS_PARAM ) +{ + flag aSign, bSign, zSign; + int32 aExp, bExp, zExp; + bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; + bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; + float128 z; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + bSig1 = extractFloat128Frac1( b ); + bSig0 = extractFloat128Frac0( b ); + bExp = extractFloat128Exp( b ); + bSign = extractFloat128Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + if ( bExp == 0x7FFF ) { + if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + goto invalid; + } + return packFloat128( zSign, 0x7FFF, 0, 0 ); + } + if ( bExp == 0x7FFF ) { + if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + return packFloat128( zSign, 0, 0, 0 ); + } + if ( bExp == 0 ) { + if ( ( bSig0 | bSig1 ) == 0 ) { + if ( ( aExp | aSig0 | aSig1 ) == 0 ) { + invalid: + float_raise( float_flag_invalid STATUS_VAR); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + float_raise( float_flag_divbyzero STATUS_VAR); + return packFloat128( zSign, 0x7FFF, 0, 0 ); + } + normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 ); + } + if ( aExp == 0 ) { + if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 ); + normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); + } + zExp = aExp - bExp + 0x3FFD; + shortShift128Left( + aSig0 | LIT64( 0x0001000000000000 ), aSig1, 15, &aSig0, &aSig1 ); + shortShift128Left( + bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 ); + if ( le128( bSig0, bSig1, aSig0, aSig1 ) ) { + shift128Right( aSig0, aSig1, 1, &aSig0, &aSig1 ); + ++zExp; + } + zSig0 = estimateDiv128To64( aSig0, aSig1, bSig0 ); + mul128By64To192( bSig0, bSig1, zSig0, &term0, &term1, &term2 ); + sub192( aSig0, aSig1, 0, term0, term1, term2, &rem0, &rem1, &rem2 ); + while ( (sbits64) rem0 < 0 ) { + --zSig0; + add192( rem0, rem1, rem2, 0, bSig0, bSig1, &rem0, &rem1, &rem2 ); + } + zSig1 = estimateDiv128To64( rem1, rem2, bSig0 ); + if ( ( zSig1 & 0x3FFF ) <= 4 ) { + mul128By64To192( bSig0, bSig1, zSig1, &term1, &term2, &term3 ); + sub192( rem1, rem2, 0, term1, term2, term3, &rem1, &rem2, &rem3 ); + while ( (sbits64) rem1 < 0 ) { + --zSig1; + add192( rem1, rem2, rem3, 0, bSig0, bSig1, &rem1, &rem2, &rem3 ); + } + zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 ); + } + shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 ); + return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the remainder of the quadruple-precision floating-point value `a' +| with respect to the corresponding value `b'. The operation is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_rem( float128 a, float128 b STATUS_PARAM ) +{ + flag aSign, bSign, zSign; + int32 aExp, bExp, expDiff; + bits64 aSig0, aSig1, bSig0, bSig1, q, term0, term1, term2; + bits64 allZero, alternateASig0, alternateASig1, sigMean1; + sbits64 sigMean0; + float128 z; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + bSig1 = extractFloat128Frac1( b ); + bSig0 = extractFloat128Frac0( b ); + bExp = extractFloat128Exp( b ); + bSign = extractFloat128Sign( b ); + if ( aExp == 0x7FFF ) { + if ( ( aSig0 | aSig1 ) + || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) { + return propagateFloat128NaN( a, b STATUS_VAR ); + } + goto invalid; + } + if ( bExp == 0x7FFF ) { + if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR ); + return a; + } + if ( bExp == 0 ) { + if ( ( bSig0 | bSig1 ) == 0 ) { + invalid: + float_raise( float_flag_invalid STATUS_VAR); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 ); + } + if ( aExp == 0 ) { + if ( ( aSig0 | aSig1 ) == 0 ) return a; + normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); + } + expDiff = aExp - bExp; + if ( expDiff < -1 ) return a; + shortShift128Left( + aSig0 | LIT64( 0x0001000000000000 ), + aSig1, + 15 - ( expDiff < 0 ), + &aSig0, + &aSig1 + ); + shortShift128Left( + bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 ); + q = le128( bSig0, bSig1, aSig0, aSig1 ); + if ( q ) sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 ); + expDiff -= 64; + while ( 0 < expDiff ) { + q = estimateDiv128To64( aSig0, aSig1, bSig0 ); + q = ( 4 < q ) ? q - 4 : 0; + mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 ); + shortShift192Left( term0, term1, term2, 61, &term1, &term2, &allZero ); + shortShift128Left( aSig0, aSig1, 61, &aSig0, &allZero ); + sub128( aSig0, 0, term1, term2, &aSig0, &aSig1 ); + expDiff -= 61; + } + if ( -64 < expDiff ) { + q = estimateDiv128To64( aSig0, aSig1, bSig0 ); + q = ( 4 < q ) ? q - 4 : 0; + q >>= - expDiff; + shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 ); + expDiff += 52; + if ( expDiff < 0 ) { + shift128Right( aSig0, aSig1, - expDiff, &aSig0, &aSig1 ); + } + else { + shortShift128Left( aSig0, aSig1, expDiff, &aSig0, &aSig1 ); + } + mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 ); + sub128( aSig0, aSig1, term1, term2, &aSig0, &aSig1 ); + } + else { + shift128Right( aSig0, aSig1, 12, &aSig0, &aSig1 ); + shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 ); + } + do { + alternateASig0 = aSig0; + alternateASig1 = aSig1; + ++q; + sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 ); + } while ( 0 <= (sbits64) aSig0 ); + add128( + aSig0, aSig1, alternateASig0, alternateASig1, &sigMean0, &sigMean1 ); + if ( ( sigMean0 < 0 ) + || ( ( ( sigMean0 | sigMean1 ) == 0 ) && ( q & 1 ) ) ) { + aSig0 = alternateASig0; + aSig1 = alternateASig1; + } + zSign = ( (sbits64) aSig0 < 0 ); + if ( zSign ) sub128( 0, 0, aSig0, aSig1, &aSig0, &aSig1 ); + return + normalizeRoundAndPackFloat128( aSign ^ zSign, bExp - 4, aSig0, aSig1 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns the square root of the quadruple-precision floating-point value `a'. +| The operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_sqrt( float128 a STATUS_PARAM ) +{ + flag aSign; + int32 aExp, zExp; + bits64 aSig0, aSig1, zSig0, zSig1, zSig2, doubleZSig0; + bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; + float128 z; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, a STATUS_VAR ); + if ( ! aSign ) return a; + goto invalid; + } + if ( aSign ) { + if ( ( aExp | aSig0 | aSig1 ) == 0 ) return a; + invalid: + float_raise( float_flag_invalid STATUS_VAR); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + if ( aExp == 0 ) { + if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( 0, 0, 0, 0 ); + normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); + } + zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFE; + aSig0 |= LIT64( 0x0001000000000000 ); + zSig0 = estimateSqrt32( aExp, aSig0>>17 ); + shortShift128Left( aSig0, aSig1, 13 - ( aExp & 1 ), &aSig0, &aSig1 ); + zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0<<32 ) + ( zSig0<<30 ); + doubleZSig0 = zSig0<<1; + mul64To128( zSig0, zSig0, &term0, &term1 ); + sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 ); + while ( (sbits64) rem0 < 0 ) { + --zSig0; + doubleZSig0 -= 2; + add128( rem0, rem1, zSig0>>63, doubleZSig0 | 1, &rem0, &rem1 ); + } + zSig1 = estimateDiv128To64( rem1, 0, doubleZSig0 ); + if ( ( zSig1 & 0x1FFF ) <= 5 ) { + if ( zSig1 == 0 ) zSig1 = 1; + mul64To128( doubleZSig0, zSig1, &term1, &term2 ); + sub128( rem1, 0, term1, term2, &rem1, &rem2 ); + mul64To128( zSig1, zSig1, &term2, &term3 ); + sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 ); + while ( (sbits64) rem1 < 0 ) { + --zSig1; + shortShift128Left( 0, zSig1, 1, &term2, &term3 ); + term3 |= 1; + term2 |= doubleZSig0; + add192( rem1, rem2, rem3, 0, term2, term3, &rem1, &rem2, &rem3 ); + } + zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 ); + } + shift128ExtraRightJamming( zSig0, zSig1, 0, 14, &zSig0, &zSig1, &zSig2 ); + return roundAndPackFloat128( 0, zExp, zSig0, zSig1, zSig2 STATUS_VAR ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is equal to +| the corresponding value `b', and 0 otherwise. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float128_eq( float128 a, float128 b STATUS_PARAM ) +{ + + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + if ( float128_is_signaling_nan( a ) + || float128_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + return + ( a.low == b.low ) + && ( ( a.high == b.high ) + || ( ( a.low == 0 ) + && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) ) + ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is less than +| or equal to the corresponding value `b', and 0 otherwise. The comparison +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float128_le( float128 a, float128 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloat128Sign( a ); + bSign = extractFloat128Sign( b ); + if ( aSign != bSign ) { + return + aSign + || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + == 0 ); + } + return + aSign ? le128( b.high, b.low, a.high, a.low ) + : le128( a.high, a.low, b.high, b.low ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is less than +| the corresponding value `b', and 0 otherwise. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float128_lt( float128 a, float128 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloat128Sign( a ); + bSign = extractFloat128Sign( b ); + if ( aSign != bSign ) { + return + aSign + && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + != 0 ); + } + return + aSign ? lt128( b.high, b.low, a.high, a.low ) + : lt128( a.high, a.low, b.high, b.low ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is equal to +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. Otherwise, the comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float128_eq_signaling( float128 a, float128 b STATUS_PARAM ) +{ + + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + float_raise( float_flag_invalid STATUS_VAR); + return 0; + } + return + ( a.low == b.low ) + && ( ( a.high == b.high ) + || ( ( a.low == 0 ) + && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) ) + ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is less than +| or equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not +| cause an exception. Otherwise, the comparison is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float128_le_quiet( float128 a, float128 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + if ( float128_is_signaling_nan( a ) + || float128_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloat128Sign( a ); + bSign = extractFloat128Sign( b ); + if ( aSign != bSign ) { + return + aSign + || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + == 0 ); + } + return + aSign ? le128( b.high, b.low, a.high, a.low ) + : le128( a.high, a.low, b.high, b.low ); + +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is less than +| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception. Otherwise, the comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +flag float128_lt_quiet( float128 a, float128 b STATUS_PARAM ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + if ( float128_is_signaling_nan( a ) + || float128_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloat128Sign( a ); + bSign = extractFloat128Sign( b ); + if ( aSign != bSign ) { + return + aSign + && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + != 0 ); + } + return + aSign ? lt128( b.high, b.low, a.high, a.low ) + : lt128( a.high, a.low, b.high, b.low ); + +} + +#endif + +/* misc functions */ +float32 uint32_to_float32( unsigned int a STATUS_PARAM ) +{ + return int64_to_float32(a STATUS_VAR); +} + +float64 uint32_to_float64( unsigned int a STATUS_PARAM ) +{ + return int64_to_float64(a STATUS_VAR); +} + +unsigned int float32_to_uint32( float32 a STATUS_PARAM ) +{ + int64_t v; + unsigned int res; + + v = float32_to_int64(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise( float_flag_invalid STATUS_VAR); + } else if (v > 0xffffffff) { + res = 0xffffffff; + float_raise( float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + +unsigned int float32_to_uint32_round_to_zero( float32 a STATUS_PARAM ) +{ + int64_t v; + unsigned int res; + + v = float32_to_int64_round_to_zero(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise( float_flag_invalid STATUS_VAR); + } else if (v > 0xffffffff) { + res = 0xffffffff; + float_raise( float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + +unsigned int float64_to_uint32( float64 a STATUS_PARAM ) +{ + int64_t v; + unsigned int res; + + v = float64_to_int64(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise( float_flag_invalid STATUS_VAR); + } else if (v > 0xffffffff) { + res = 0xffffffff; + float_raise( float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + +unsigned int float64_to_uint32_round_to_zero( float64 a STATUS_PARAM ) +{ + int64_t v; + unsigned int res; + + v = float64_to_int64_round_to_zero(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise( float_flag_invalid STATUS_VAR); + } else if (v > 0xffffffff) { + res = 0xffffffff; + float_raise( float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + +#define COMPARE(s, nan_exp) \ +INLINE char float ## s ## _compare_internal( float ## s a, float ## s b, \ + int is_quiet STATUS_PARAM ) \ +{ \ + flag aSign, bSign; \ + \ + if (( ( extractFloat ## s ## Exp( a ) == nan_exp ) && \ + extractFloat ## s ## Frac( a ) ) || \ + ( ( extractFloat ## s ## Exp( b ) == nan_exp ) && \ + extractFloat ## s ## Frac( b ) )) { \ + if (!is_quiet || \ + float ## s ## _is_signaling_nan( a ) || \ + float ## s ## _is_signaling_nan( b ) ) { \ + float_raise( float_flag_invalid STATUS_VAR); \ + } \ + return float_relation_unordered; \ + } \ + aSign = extractFloat ## s ## Sign( a ); \ + bSign = extractFloat ## s ## Sign( b ); \ + if ( aSign != bSign ) { \ + if ( (bits ## s) ( ( a | b )<<1 ) == 0 ) { \ + /* zero case */ \ + return float_relation_equal; \ + } else { \ + return 1 - (2 * aSign); \ + } \ + } else { \ + if (a == b) { \ + return float_relation_equal; \ + } else { \ + return 1 - 2 * (aSign ^ ( a < b )); \ + } \ + } \ +} \ + \ +char float ## s ## _compare( float ## s a, float ## s b STATUS_PARAM ) \ +{ \ + return float ## s ## _compare_internal(a, b, 0 STATUS_VAR); \ +} \ + \ +char float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM ) \ +{ \ + return float ## s ## _compare_internal(a, b, 1 STATUS_VAR); \ +} + +COMPARE(32, 0xff) +COMPARE(64, 0x7ff) diff --git a/tools/ioemu/fpu/softfloat.h b/tools/ioemu/fpu/softfloat.h new file mode 100644 index 0000000000..fdc80f32db --- /dev/null +++ b/tools/ioemu/fpu/softfloat.h @@ -0,0 +1,398 @@ +/*============================================================================ + +This C header file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic +Package, Release 2b. + +Written by John R. Hauser. This work was made possible in part by the +International Computer Science Institute, located at Suite 600, 1947 Center +Street, Berkeley, California 94704. Funding was partially provided by the +National Science Foundation under grant MIP-9311980. The original version +of this code was written as part of a project to build a fixed-point vector +processor in collaboration with the University of California at Berkeley, +overseen by Profs. Nelson Morgan and John Wawrzynek. More information +is available through the Web page `http://www.cs.berkeley.edu/~jhauser/ +arithmetic/SoftFloat.html'. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has +been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES +RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS +AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES, +COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE +EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE +INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR +OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE. + +Derivative works are acceptable, even for commercial purposes, so long as +(1) the source code for the derivative work includes prominent notice that +the work is derivative, and (2) the source code includes prominent notice with +these four paragraphs for those parts of this code that are retained. + +=============================================================================*/ + +#ifndef SOFTFLOAT_H +#define SOFTFLOAT_H + +#include +#include "config.h" + +/*---------------------------------------------------------------------------- +| Each of the following `typedef's defines the most convenient type that holds +| integers of at least as many bits as specified. For example, `uint8' should +| be the most convenient type that can hold unsigned integers of as many as +| 8 bits. The `flag' type must be able to hold either a 0 or 1. For most +| implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed +| to the same as `int'. +*----------------------------------------------------------------------------*/ +typedef char flag; +typedef uint8_t uint8; +typedef int8_t int8; +typedef int uint16; +typedef int int16; +typedef unsigned int uint32; +typedef signed int int32; +typedef uint64_t uint64; +typedef int64_t int64; + +/*---------------------------------------------------------------------------- +| Each of the following `typedef's defines a type that holds integers +| of _exactly_ the number of bits specified. For instance, for most +| implementation of C, `bits16' and `sbits16' should be `typedef'ed to +| `unsigned short int' and `signed short int' (or `short int'), respectively. +*----------------------------------------------------------------------------*/ +typedef uint8_t bits8; +typedef int8_t sbits8; +typedef uint16_t bits16; +typedef int16_t sbits16; +typedef uint32_t bits32; +typedef int32_t sbits32; +typedef uint64_t bits64; +typedef int64_t sbits64; + +#define LIT64( a ) a##LL +#define INLINE static inline + +/*---------------------------------------------------------------------------- +| The macro `FLOATX80' must be defined to enable the extended double-precision +| floating-point format `floatx80'. If this macro is not defined, the +| `floatx80' type will not be defined, and none of the functions that either +| input or output the `floatx80' type will be defined. The same applies to +| the `FLOAT128' macro and the quadruple-precision format `float128'. +*----------------------------------------------------------------------------*/ +#ifdef CONFIG_SOFTFLOAT +/* bit exact soft float support */ +#define FLOATX80 +#define FLOAT128 +#else +/* native float support */ +#if (defined(__i386__) || defined(__x86_64__)) && !defined(_BSD) +#define FLOATX80 +#endif +#endif /* !CONFIG_SOFTFLOAT */ + +#define STATUS_PARAM , float_status *status +#define STATUS(field) status->field +#define STATUS_VAR , status + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE floating-point ordering relations +*----------------------------------------------------------------------------*/ +enum { + float_relation_less = -1, + float_relation_equal = 0, + float_relation_greater = 1, + float_relation_unordered = 2 +}; + +#ifdef CONFIG_SOFTFLOAT +/*---------------------------------------------------------------------------- +| Software IEC/IEEE floating-point types. +*----------------------------------------------------------------------------*/ +typedef uint32_t float32; +typedef uint64_t float64; +#ifdef FLOATX80 +typedef struct { + uint64_t low; + uint16_t high; +} floatx80; +#endif +#ifdef FLOAT128 +typedef struct { +#ifdef WORDS_BIGENDIAN + uint64_t high, low; +#else + uint64_t low, high; +#endif +} float128; +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE floating-point underflow tininess-detection mode. +*----------------------------------------------------------------------------*/ +enum { + float_tininess_after_rounding = 0, + float_tininess_before_rounding = 1 +}; + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE floating-point rounding mode. +*----------------------------------------------------------------------------*/ +enum { + float_round_nearest_even = 0, + float_round_down = 1, + float_round_up = 2, + float_round_to_zero = 3 +}; + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE floating-point exception flags. +*----------------------------------------------------------------------------*/ +enum { + float_flag_invalid = 1, + float_flag_divbyzero = 4, + float_flag_overflow = 8, + float_flag_underflow = 16, + float_flag_inexact = 32 +}; + +typedef struct float_status { + signed char float_detect_tininess; + signed char float_rounding_mode; + signed char float_exception_flags; +#ifdef FLOATX80 + signed char floatx80_rounding_precision; +#endif +} float_status; + +void set_float_rounding_mode(int val STATUS_PARAM); +void set_float_exception_flags(int val STATUS_PARAM); +INLINE int get_float_exception_flags(float_status *status) +{ + return STATUS(float_exception_flags); +} +#ifdef FLOATX80 +void set_floatx80_rounding_precision(int val STATUS_PARAM); +#endif + +/*---------------------------------------------------------------------------- +| Routine to raise any or all of the software IEC/IEEE floating-point +| exception flags. +*----------------------------------------------------------------------------*/ +void float_raise( int8 flags STATUS_PARAM); + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE integer-to-floating-point conversion routines. +*----------------------------------------------------------------------------*/ +float32 int32_to_float32( int STATUS_PARAM ); +float64 int32_to_float64( int STATUS_PARAM ); +float32 uint32_to_float32( unsigned int STATUS_PARAM ); +float64 uint32_to_float64( unsigned int STATUS_PARAM ); +#ifdef FLOATX80 +floatx80 int32_to_floatx80( int STATUS_PARAM ); +#endif +#ifdef FLOAT128 +float128 int32_to_float128( int STATUS_PARAM ); +#endif +float32 int64_to_float32( int64_t STATUS_PARAM ); +float64 int64_to_float64( int64_t STATUS_PARAM ); +#ifdef FLOATX80 +floatx80 int64_to_floatx80( int64_t STATUS_PARAM ); +#endif +#ifdef FLOAT128 +float128 int64_to_float128( int64_t STATUS_PARAM ); +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE single-precision conversion routines. +*----------------------------------------------------------------------------*/ +int float32_to_int32( float32 STATUS_PARAM ); +int float32_to_int32_round_to_zero( float32 STATUS_PARAM ); +unsigned int float32_to_uint32( float32 STATUS_PARAM ); +unsigned int float32_to_uint32_round_to_zero( float32 STATUS_PARAM ); +int64_t float32_to_int64( float32 STATUS_PARAM ); +int64_t float32_to_int64_round_to_zero( float32 STATUS_PARAM ); +float64 float32_to_float64( float32 STATUS_PARAM ); +#ifdef FLOATX80 +floatx80 float32_to_floatx80( float32 STATUS_PARAM ); +#endif +#ifdef FLOAT128 +float128 float32_to_float128( float32 STATUS_PARAM ); +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE single-precision operations. +*----------------------------------------------------------------------------*/ +float32 float32_round_to_int( float32 STATUS_PARAM ); +float32 float32_add( float32, float32 STATUS_PARAM ); +float32 float32_sub( float32, float32 STATUS_PARAM ); +float32 float32_mul( float32, float32 STATUS_PARAM ); +float32 float32_div( float32, float32 STATUS_PARAM ); +float32 float32_rem( float32, float32 STATUS_PARAM ); +float32 float32_sqrt( float32 STATUS_PARAM ); +char float32_eq( float32, float32 STATUS_PARAM ); +char float32_le( float32, float32 STATUS_PARAM ); +char float32_lt( float32, float32 STATUS_PARAM ); +char float32_eq_signaling( float32, float32 STATUS_PARAM ); +char float32_le_quiet( float32, float32 STATUS_PARAM ); +char float32_lt_quiet( float32, float32 STATUS_PARAM ); +char float32_compare( float32, float32 STATUS_PARAM ); +char float32_compare_quiet( float32, float32 STATUS_PARAM ); +char float32_is_signaling_nan( float32 ); + +INLINE float32 float32_abs(float32 a) +{ + return a & 0x7fffffff; +} + +INLINE float32 float32_chs(float32 a) +{ + return a ^ 0x80000000; +} + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE double-precision conversion routines. +*----------------------------------------------------------------------------*/ +int float64_to_int32( float64 STATUS_PARAM ); +int float64_to_int32_round_to_zero( float64 STATUS_PARAM ); +unsigned int float64_to_uint32( float64 STATUS_PARAM ); +unsigned int float64_to_uint32_round_to_zero( float64 STATUS_PARAM ); +int64_t float64_to_int64( float64 STATUS_PARAM ); +int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM ); +float32 float64_to_float32( float64 STATUS_PARAM ); +#ifdef FLOATX80 +floatx80 float64_to_floatx80( float64 STATUS_PARAM ); +#endif +#ifdef FLOAT128 +float128 float64_to_float128( float64 STATUS_PARAM ); +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE double-precision operations. +*----------------------------------------------------------------------------*/ +float64 float64_round_to_int( float64 STATUS_PARAM ); +float64 float64_add( float64, float64 STATUS_PARAM ); +float64 float64_sub( float64, float64 STATUS_PARAM ); +float64 float64_mul( float64, float64 STATUS_PARAM ); +float64 float64_div( float64, float64 STATUS_PARAM ); +float64 float64_rem( float64, float64 STATUS_PARAM ); +float64 float64_sqrt( float64 STATUS_PARAM ); +char float64_eq( float64, float64 STATUS_PARAM ); +char float64_le( float64, float64 STATUS_PARAM ); +char float64_lt( float64, float64 STATUS_PARAM ); +char float64_eq_signaling( float64, float64 STATUS_PARAM ); +char float64_le_quiet( float64, float64 STATUS_PARAM ); +char float64_lt_quiet( float64, float64 STATUS_PARAM ); +char float64_compare( float64, float64 STATUS_PARAM ); +char float64_compare_quiet( float64, float64 STATUS_PARAM ); +char float64_is_signaling_nan( float64 ); + +INLINE float64 float64_abs(float64 a) +{ + return a & 0x7fffffffffffffffLL; +} + +INLINE float64 float64_chs(float64 a) +{ + return a ^ 0x8000000000000000LL; +} + +#ifdef FLOATX80 + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE extended double-precision conversion routines. +*----------------------------------------------------------------------------*/ +int floatx80_to_int32( floatx80 STATUS_PARAM ); +int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM ); +int64_t floatx80_to_int64( floatx80 STATUS_PARAM ); +int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM ); +float32 floatx80_to_float32( floatx80 STATUS_PARAM ); +float64 floatx80_to_float64( floatx80 STATUS_PARAM ); +#ifdef FLOAT128 +float128 floatx80_to_float128( floatx80 STATUS_PARAM ); +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE extended double-precision operations. +*----------------------------------------------------------------------------*/ +floatx80 floatx80_round_to_int( floatx80 STATUS_PARAM ); +floatx80 floatx80_add( floatx80, floatx80 STATUS_PARAM ); +floatx80 floatx80_sub( floatx80, floatx80 STATUS_PARAM ); +floatx80 floatx80_mul( floatx80, floatx80 STATUS_PARAM ); +floatx80 floatx80_div( floatx80, floatx80 STATUS_PARAM ); +floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM ); +floatx80 floatx80_sqrt( floatx80 STATUS_PARAM ); +char floatx80_eq( floatx80, floatx80 STATUS_PARAM ); +char floatx80_le( floatx80, floatx80 STATUS_PARAM ); +char floatx80_lt( floatx80, floatx80 STATUS_PARAM ); +char floatx80_eq_signaling( floatx80, floatx80 STATUS_PARAM ); +char floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM ); +char floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM ); +char floatx80_is_signaling_nan( floatx80 ); + +INLINE floatx80 floatx80_abs(floatx80 a) +{ + a.high &= 0x7fff; + return a; +} + +INLINE floatx80 floatx80_chs(floatx80 a) +{ + a.high ^= 0x8000; + return a; +} + +#endif + +#ifdef FLOAT128 + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE quadruple-precision conversion routines. +*----------------------------------------------------------------------------*/ +int float128_to_int32( float128 STATUS_PARAM ); +int float128_to_int32_round_to_zero( float128 STATUS_PARAM ); +int64_t float128_to_int64( float128 STATUS_PARAM ); +int64_t float128_to_int64_round_to_zero( float128 STATUS_PARAM ); +float32 float128_to_float32( float128 STATUS_PARAM ); +float64 float128_to_float64( float128 STATUS_PARAM ); +#ifdef FLOATX80 +floatx80 float128_to_floatx80( float128 STATUS_PARAM ); +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE quadruple-precision operations. +*----------------------------------------------------------------------------*/ +float128 float128_round_to_int( float128 STATUS_PARAM ); +float128 float128_add( float128, float128 STATUS_PARAM ); +float128 float128_sub( float128, float128 STATUS_PARAM ); +float128 float128_mul( float128, float128 STATUS_PARAM ); +float128 float128_div( float128, float128 STATUS_PARAM ); +float128 float128_rem( float128, float128 STATUS_PARAM ); +float128 float128_sqrt( float128 STATUS_PARAM ); +char float128_eq( float128, float128 STATUS_PARAM ); +char float128_le( float128, float128 STATUS_PARAM ); +char float128_lt( float128, float128 STATUS_PARAM ); +char float128_eq_signaling( float128, float128 STATUS_PARAM ); +char float128_le_quiet( float128, float128 STATUS_PARAM ); +char float128_lt_quiet( float128, float128 STATUS_PARAM ); +char float128_is_signaling_nan( float128 ); + +INLINE float128 float128_abs(float128 a) +{ + a.high &= 0x7fffffffffffffffLL; + return a; +} + +INLINE float128 float128_chs(float128 a) +{ + a.high ^= 0x8000000000000000LL; + return a; +} + +#endif + +#else /* CONFIG_SOFTFLOAT */ + +#include "softfloat-native.h" + +#endif /* !CONFIG_SOFTFLOAT */ + +#endif /* !SOFTFLOAT_H */ diff --git a/tools/ioemu/gdbstub.c b/tools/ioemu/gdbstub.c new file mode 100644 index 0000000000..bca9b1e2a6 --- /dev/null +++ b/tools/ioemu/gdbstub.c @@ -0,0 +1,943 @@ +/* + * gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifdef CONFIG_USER_ONLY +#include +#include +#include +#include +#include +#include + +#include "qemu.h" +#else +#include "vl.h" +#endif + +#include +#include +#include +#include + +//#define DEBUG_GDB + +enum RSState { + RS_IDLE, + RS_GETLINE, + RS_CHKSUM1, + RS_CHKSUM2, +}; +/* XXX: This is not thread safe. Do we care? */ +static int gdbserver_fd = -1; + +typedef struct GDBState { + CPUState *env; /* current CPU */ + enum RSState state; /* parsing state */ + int fd; + char line_buf[4096]; + int line_buf_index; + int line_csum; +#ifdef CONFIG_USER_ONLY + int running_state; +#endif +} GDBState; + +#ifdef CONFIG_USER_ONLY +/* XXX: remove this hack. */ +static GDBState gdbserver_state; +#endif + +static int get_char(GDBState *s) +{ + uint8_t ch; + int ret; + + for(;;) { + ret = read(s->fd, &ch, 1); + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) + return -1; + } else if (ret == 0) { + return -1; + } else { + break; + } + } + return ch; +} + +static void put_buffer(GDBState *s, const uint8_t *buf, int len) +{ + int ret; + + while (len > 0) { + ret = write(s->fd, buf, len); + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) + return; + } else { + buf += ret; + len -= ret; + } + } +} + +static inline int fromhex(int v) +{ + if (v >= '0' && v <= '9') + return v - '0'; + else if (v >= 'A' && v <= 'F') + return v - 'A' + 10; + else if (v >= 'a' && v <= 'f') + return v - 'a' + 10; + else + return 0; +} + +static inline int tohex(int v) +{ + if (v < 10) + return v + '0'; + else + return v - 10 + 'a'; +} + +static void memtohex(char *buf, const uint8_t *mem, int len) +{ + int i, c; + char *q; + q = buf; + for(i = 0; i < len; i++) { + c = mem[i]; + *q++ = tohex(c >> 4); + *q++ = tohex(c & 0xf); + } + *q = '\0'; +} + +static void hextomem(uint8_t *mem, const char *buf, int len) +{ + int i; + + for(i = 0; i < len; i++) { + mem[i] = (fromhex(buf[0]) << 4) | fromhex(buf[1]); + buf += 2; + } +} + +/* return -1 if error, 0 if OK */ +static int put_packet(GDBState *s, char *buf) +{ + char buf1[3]; + int len, csum, ch, i; + +#ifdef DEBUG_GDB + printf("reply='%s'\n", buf); +#endif + + for(;;) { + buf1[0] = '$'; + put_buffer(s, buf1, 1); + len = strlen(buf); + put_buffer(s, buf, len); + csum = 0; + for(i = 0; i < len; i++) { + csum += buf[i]; + } + buf1[0] = '#'; + buf1[1] = tohex((csum >> 4) & 0xf); + buf1[2] = tohex((csum) & 0xf); + + put_buffer(s, buf1, 3); + + ch = get_char(s); + if (ch < 0) + return -1; + if (ch == '+') + break; + } + return 0; +} + +#if defined(TARGET_I386) + +static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) +{ + uint32_t *registers = (uint32_t *)mem_buf; + int i, fpus; + + for(i = 0; i < 8; i++) { + registers[i] = env->regs[i]; + } + registers[8] = env->eip; + registers[9] = env->eflags; + registers[10] = env->segs[R_CS].selector; + registers[11] = env->segs[R_SS].selector; + registers[12] = env->segs[R_DS].selector; + registers[13] = env->segs[R_ES].selector; + registers[14] = env->segs[R_FS].selector; + registers[15] = env->segs[R_GS].selector; + /* XXX: convert floats */ + for(i = 0; i < 8; i++) { + memcpy(mem_buf + 16 * 4 + i * 10, &env->fpregs[i], 10); + } + registers[36] = env->fpuc; + fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + registers[37] = fpus; + registers[38] = 0; /* XXX: convert tags */ + registers[39] = 0; /* fiseg */ + registers[40] = 0; /* fioff */ + registers[41] = 0; /* foseg */ + registers[42] = 0; /* fooff */ + registers[43] = 0; /* fop */ + + for(i = 0; i < 16; i++) + tswapls(®isters[i]); + for(i = 36; i < 44; i++) + tswapls(®isters[i]); + return 44 * 4; +} + +static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) +{ + uint32_t *registers = (uint32_t *)mem_buf; + int i; + + for(i = 0; i < 8; i++) { + env->regs[i] = tswapl(registers[i]); + } + env->eip = tswapl(registers[8]); + env->eflags = tswapl(registers[9]); +#if defined(CONFIG_USER_ONLY) +#define LOAD_SEG(index, sreg)\ + if (tswapl(registers[index]) != env->segs[sreg].selector)\ + cpu_x86_load_seg(env, sreg, tswapl(registers[index])); + LOAD_SEG(10, R_CS); + LOAD_SEG(11, R_SS); + LOAD_SEG(12, R_DS); + LOAD_SEG(13, R_ES); + LOAD_SEG(14, R_FS); + LOAD_SEG(15, R_GS); +#endif +} + +#elif defined (TARGET_PPC) +static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) +{ + uint32_t *registers = (uint32_t *)mem_buf, tmp; + int i; + + /* fill in gprs */ + for(i = 0; i < 32; i++) { + registers[i] = tswapl(env->gpr[i]); + } + /* fill in fprs */ + for (i = 0; i < 32; i++) { + registers[(i * 2) + 32] = tswapl(*((uint32_t *)&env->fpr[i])); + registers[(i * 2) + 33] = tswapl(*((uint32_t *)&env->fpr[i] + 1)); + } + /* nip, msr, ccr, lnk, ctr, xer, mq */ + registers[96] = tswapl(env->nip); + registers[97] = tswapl(do_load_msr(env)); + tmp = 0; + for (i = 0; i < 8; i++) + tmp |= env->crf[i] << (32 - ((i + 1) * 4)); + registers[98] = tswapl(tmp); + registers[99] = tswapl(env->lr); + registers[100] = tswapl(env->ctr); + registers[101] = tswapl(do_load_xer(env)); + registers[102] = 0; + + return 103 * 4; +} + +static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) +{ + uint32_t *registers = (uint32_t *)mem_buf; + int i; + + /* fill in gprs */ + for (i = 0; i < 32; i++) { + env->gpr[i] = tswapl(registers[i]); + } + /* fill in fprs */ + for (i = 0; i < 32; i++) { + *((uint32_t *)&env->fpr[i]) = tswapl(registers[(i * 2) + 32]); + *((uint32_t *)&env->fpr[i] + 1) = tswapl(registers[(i * 2) + 33]); + } + /* nip, msr, ccr, lnk, ctr, xer, mq */ + env->nip = tswapl(registers[96]); + do_store_msr(env, tswapl(registers[97])); + registers[98] = tswapl(registers[98]); + for (i = 0; i < 8; i++) + env->crf[i] = (registers[98] >> (32 - ((i + 1) * 4))) & 0xF; + env->lr = tswapl(registers[99]); + env->ctr = tswapl(registers[100]); + do_store_xer(env, tswapl(registers[101])); +} +#elif defined (TARGET_SPARC) +static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) +{ + target_ulong *registers = (target_ulong *)mem_buf; + int i; + + /* fill in g0..g7 */ + for(i = 0; i < 8; i++) { + registers[i] = tswapl(env->gregs[i]); + } + /* fill in register window */ + for(i = 0; i < 24; i++) { + registers[i + 8] = tswapl(env->regwptr[i]); + } + /* fill in fprs */ + for (i = 0; i < 32; i++) { + registers[i + 32] = tswapl(*((uint32_t *)&env->fpr[i])); + } +#ifndef TARGET_SPARC64 + /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ + registers[64] = tswapl(env->y); + { + target_ulong tmp; + + tmp = GET_PSR(env); + registers[65] = tswapl(tmp); + } + registers[66] = tswapl(env->wim); + registers[67] = tswapl(env->tbr); + registers[68] = tswapl(env->pc); + registers[69] = tswapl(env->npc); + registers[70] = tswapl(env->fsr); + registers[71] = 0; /* csr */ + registers[72] = 0; + return 73 * sizeof(target_ulong); +#else + for (i = 0; i < 32; i += 2) { + registers[i/2 + 64] = tswapl(*((uint64_t *)&env->fpr[i])); + } + registers[81] = tswapl(env->pc); + registers[82] = tswapl(env->npc); + registers[83] = tswapl(env->tstate[env->tl]); + registers[84] = tswapl(env->fsr); + registers[85] = tswapl(env->fprs); + registers[86] = tswapl(env->y); + return 87 * sizeof(target_ulong); +#endif +} + +static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) +{ + target_ulong *registers = (target_ulong *)mem_buf; + int i; + + /* fill in g0..g7 */ + for(i = 0; i < 7; i++) { + env->gregs[i] = tswapl(registers[i]); + } + /* fill in register window */ + for(i = 0; i < 24; i++) { + env->regwptr[i] = tswapl(registers[i + 8]); + } + /* fill in fprs */ + for (i = 0; i < 32; i++) { + *((uint32_t *)&env->fpr[i]) = tswapl(registers[i + 32]); + } +#ifndef TARGET_SPARC64 + /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ + env->y = tswapl(registers[64]); + PUT_PSR(env, tswapl(registers[65])); + env->wim = tswapl(registers[66]); + env->tbr = tswapl(registers[67]); + env->pc = tswapl(registers[68]); + env->npc = tswapl(registers[69]); + env->fsr = tswapl(registers[70]); +#else + for (i = 0; i < 32; i += 2) { + uint64_t tmp; + tmp = tswapl(registers[i/2 + 64]) << 32; + tmp |= tswapl(registers[i/2 + 64 + 1]); + *((uint64_t *)&env->fpr[i]) = tmp; + } + env->pc = tswapl(registers[81]); + env->npc = tswapl(registers[82]); + env->tstate[env->tl] = tswapl(registers[83]); + env->fsr = tswapl(registers[84]); + env->fprs = tswapl(registers[85]); + env->y = tswapl(registers[86]); +#endif +} +#elif defined (TARGET_ARM) +static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) +{ + int i; + uint8_t *ptr; + + ptr = mem_buf; + /* 16 core integer registers (4 bytes each). */ + for (i = 0; i < 16; i++) + { + *(uint32_t *)ptr = tswapl(env->regs[i]); + ptr += 4; + } + /* 8 FPA registers (12 bytes each), FPS (4 bytes). + Not yet implemented. */ + memset (ptr, 0, 8 * 12 + 4); + ptr += 8 * 12 + 4; + /* CPSR (4 bytes). */ + *(uint32_t *)ptr = tswapl (cpsr_read(env)); + ptr += 4; + + return ptr - mem_buf; +} + +static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) +{ + int i; + uint8_t *ptr; + + ptr = mem_buf; + /* Core integer registers. */ + for (i = 0; i < 16; i++) + { + env->regs[i] = tswapl(*(uint32_t *)ptr); + ptr += 4; + } + /* Ignore FPA regs and scr. */ + ptr += 8 * 12 + 4; + cpsr_write (env, tswapl(*(uint32_t *)ptr), 0xffffffff); +} +#elif defined (TARGET_MIPS) +static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) +{ + int i; + uint8_t *ptr; + + ptr = mem_buf; + for (i = 0; i < 32; i++) + { + *(uint32_t *)ptr = tswapl(env->gpr[i]); + ptr += 4; + } + + *(uint32_t *)ptr = tswapl(env->CP0_Status); + ptr += 4; + + *(uint32_t *)ptr = tswapl(env->LO); + ptr += 4; + + *(uint32_t *)ptr = tswapl(env->HI); + ptr += 4; + + *(uint32_t *)ptr = tswapl(env->CP0_BadVAddr); + ptr += 4; + + *(uint32_t *)ptr = tswapl(env->CP0_Cause); + ptr += 4; + + *(uint32_t *)ptr = tswapl(env->PC); + ptr += 4; + + /* 32 FP registers, fsr, fir, fp. Not yet implemented. */ + + return ptr - mem_buf; +} + +static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) +{ + int i; + uint8_t *ptr; + + ptr = mem_buf; + for (i = 0; i < 32; i++) + { + env->gpr[i] = tswapl(*(uint32_t *)ptr); + ptr += 4; + } + + env->CP0_Status = tswapl(*(uint32_t *)ptr); + ptr += 4; + + env->LO = tswapl(*(uint32_t *)ptr); + ptr += 4; + + env->HI = tswapl(*(uint32_t *)ptr); + ptr += 4; + + env->CP0_BadVAddr = tswapl(*(uint32_t *)ptr); + ptr += 4; + + env->CP0_Cause = tswapl(*(uint32_t *)ptr); + ptr += 4; + + env->PC = tswapl(*(uint32_t *)ptr); + ptr += 4; +} +#elif defined (TARGET_SH4) +static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) +{ + uint32_t *ptr = (uint32_t *)mem_buf; + int i; + +#define SAVE(x) *ptr++=tswapl(x) + for (i = 0; i < 16; i++) SAVE(env->gregs[i]); + SAVE (env->pc); + SAVE (env->pr); + SAVE (env->gbr); + SAVE (env->vbr); + SAVE (env->mach); + SAVE (env->macl); + SAVE (env->sr); + SAVE (0); /* TICKS */ + SAVE (0); /* STALLS */ + SAVE (0); /* CYCLES */ + SAVE (0); /* INSTS */ + SAVE (0); /* PLR */ + + return ((uint8_t *)ptr - mem_buf); +} + +static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) +{ + uint32_t *ptr = (uint32_t *)mem_buf; + int i; + +#define LOAD(x) (x)=*ptr++; + for (i = 0; i < 16; i++) LOAD(env->gregs[i]); + LOAD (env->pc); + LOAD (env->pr); + LOAD (env->gbr); + LOAD (env->vbr); + LOAD (env->mach); + LOAD (env->macl); + LOAD (env->sr); +} +#else +static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) +{ + return 0; +} + +static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) +{ +} + +#endif + +static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf) +{ + const char *p; + int ch, reg_size, type; + char buf[4096]; + uint8_t mem_buf[2000]; + uint32_t *registers; + uint32_t addr, len; + +#ifdef DEBUG_GDB + printf("command='%s'\n", line_buf); +#endif + p = line_buf; + ch = *p++; + switch(ch) { + case '?': + /* TODO: Make this return the correct value for user-mode. */ + snprintf(buf, sizeof(buf), "S%02x", SIGTRAP); + put_packet(s, buf); + break; + case 'c': + if (*p != '\0') { + addr = strtoul(p, (char **)&p, 16); +#if defined(TARGET_I386) + env->eip = addr; +#elif defined (TARGET_PPC) + env->nip = addr; +#elif defined (TARGET_SPARC) + env->pc = addr; + env->npc = addr + 4; +#elif defined (TARGET_ARM) + env->regs[15] = addr; +#elif defined (TARGET_SH4) + env->pc = addr; +#endif + } +#ifdef CONFIG_USER_ONLY + s->running_state = 1; +#else + vm_start(); +#endif + return RS_IDLE; + case 's': + if (*p != '\0') { + addr = strtoul(p, (char **)&p, 16); +#if defined(TARGET_I386) + env->eip = addr; +#elif defined (TARGET_PPC) + env->nip = addr; +#elif defined (TARGET_SPARC) + env->pc = addr; + env->npc = addr + 4; +#elif defined (TARGET_ARM) + env->regs[15] = addr; +#elif defined (TARGET_SH4) + env->pc = addr; +#endif + } + cpu_single_step(env, 1); +#ifdef CONFIG_USER_ONLY + s->running_state = 1; +#else + vm_start(); +#endif + return RS_IDLE; + case 'g': + reg_size = cpu_gdb_read_registers(env, mem_buf); + memtohex(buf, mem_buf, reg_size); + put_packet(s, buf); + break; + case 'G': + registers = (void *)mem_buf; + len = strlen(p) / 2; + hextomem((uint8_t *)registers, p, len); + cpu_gdb_write_registers(env, mem_buf, len); + put_packet(s, "OK"); + break; + case 'm': + addr = strtoul(p, (char **)&p, 16); + if (*p == ',') + p++; + len = strtoul(p, NULL, 16); + if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0) { + put_packet (s, "E14"); + } else { + memtohex(buf, mem_buf, len); + put_packet(s, buf); + } + break; + case 'M': + addr = strtoul(p, (char **)&p, 16); + if (*p == ',') + p++; + len = strtoul(p, (char **)&p, 16); + if (*p == ':') + p++; + hextomem(mem_buf, p, len); + if (cpu_memory_rw_debug(env, addr, mem_buf, len, 1) != 0) + put_packet(s, "E14"); + else + put_packet(s, "OK"); + break; + case 'Z': + type = strtoul(p, (char **)&p, 16); + if (*p == ',') + p++; + addr = strtoul(p, (char **)&p, 16); + if (*p == ',') + p++; + len = strtoul(p, (char **)&p, 16); + if (type == 0 || type == 1) { + if (cpu_breakpoint_insert(env, addr) < 0) + goto breakpoint_error; + put_packet(s, "OK"); + } else { + breakpoint_error: + put_packet(s, "E22"); + } + break; + case 'z': + type = strtoul(p, (char **)&p, 16); + if (*p == ',') + p++; + addr = strtoul(p, (char **)&p, 16); + if (*p == ',') + p++; + len = strtoul(p, (char **)&p, 16); + if (type == 0 || type == 1) { + cpu_breakpoint_remove(env, addr); + put_packet(s, "OK"); + } else { + goto breakpoint_error; + } + break; + default: + // unknown_command: + /* put empty packet */ + buf[0] = '\0'; + put_packet(s, buf); + break; + } + return RS_IDLE; +} + +extern void tb_flush(CPUState *env); + +#ifndef CONFIG_USER_ONLY +static void gdb_vm_stopped(void *opaque, int reason) +{ + GDBState *s = opaque; + char buf[256]; + int ret; + + /* disable single step if it was enable */ + cpu_single_step(s->env, 0); + + if (reason == EXCP_DEBUG) { + tb_flush(s->env); + ret = SIGTRAP; + } else if (reason == EXCP_INTERRUPT) { + ret = SIGINT; + } else { + ret = 0; + } + snprintf(buf, sizeof(buf), "S%02x", ret); + put_packet(s, buf); +} +#endif + +static void gdb_read_byte(GDBState *s, int ch) +{ + CPUState *env = s->env; + int i, csum; + char reply[1]; + +#ifndef CONFIG_USER_ONLY + if (vm_running) { + /* when the CPU is running, we cannot do anything except stop + it when receiving a char */ + vm_stop(EXCP_INTERRUPT); + } else +#endif + { + switch(s->state) { + case RS_IDLE: + if (ch == '$') { + s->line_buf_index = 0; + s->state = RS_GETLINE; + } + break; + case RS_GETLINE: + if (ch == '#') { + s->state = RS_CHKSUM1; + } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { + s->state = RS_IDLE; + } else { + s->line_buf[s->line_buf_index++] = ch; + } + break; + case RS_CHKSUM1: + s->line_buf[s->line_buf_index] = '\0'; + s->line_csum = fromhex(ch) << 4; + s->state = RS_CHKSUM2; + break; + case RS_CHKSUM2: + s->line_csum |= fromhex(ch); + csum = 0; + for(i = 0; i < s->line_buf_index; i++) { + csum += s->line_buf[i]; + } + if (s->line_csum != (csum & 0xff)) { + reply[0] = '-'; + put_buffer(s, reply, 1); + s->state = RS_IDLE; + } else { + reply[0] = '+'; + put_buffer(s, reply, 1); + s->state = gdb_handle_packet(s, env, s->line_buf); + } + break; + } + } +} + +#ifdef CONFIG_USER_ONLY +int +gdb_handlesig (CPUState *env, int sig) +{ + GDBState *s; + char buf[256]; + int n; + + if (gdbserver_fd < 0) + return sig; + + s = &gdbserver_state; + + /* disable single step if it was enabled */ + cpu_single_step(env, 0); + tb_flush(env); + + if (sig != 0) + { + snprintf(buf, sizeof(buf), "S%02x", sig); + put_packet(s, buf); + } + + sig = 0; + s->state = RS_IDLE; + s->running_state = 0; + while (s->running_state == 0) { + n = read (s->fd, buf, 256); + if (n > 0) + { + int i; + + for (i = 0; i < n; i++) + gdb_read_byte (s, buf[i]); + } + else if (n == 0 || errno != EAGAIN) + { + /* XXX: Connection closed. Should probably wait for annother + connection before continuing. */ + return sig; + } + } + return sig; +} + +/* Tell the remote gdb that the process has exited. */ +void gdb_exit(CPUState *env, int code) +{ + GDBState *s; + char buf[4]; + + if (gdbserver_fd < 0) + return; + + s = &gdbserver_state; + + snprintf(buf, sizeof(buf), "W%02x", code); + put_packet(s, buf); +} + +#else +static void gdb_read(void *opaque) +{ + GDBState *s = opaque; + int i, size; + uint8_t buf[4096]; + + size = read(s->fd, buf, sizeof(buf)); + if (size < 0) + return; + if (size == 0) { + /* end of connection */ + qemu_del_vm_stop_handler(gdb_vm_stopped, s); + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + qemu_free(s); + vm_start(); + } else { + for(i = 0; i < size; i++) + gdb_read_byte(s, buf[i]); + } +} + +#endif + +static void gdb_accept(void *opaque) +{ + GDBState *s; + struct sockaddr_in sockaddr; + socklen_t len; + int val, fd; + + for(;;) { + len = sizeof(sockaddr); + fd = accept(gdbserver_fd, (struct sockaddr *)&sockaddr, &len); + if (fd < 0 && errno != EINTR) { + perror("accept"); + return; + } else if (fd >= 0) { + break; + } + } + + /* set short latency */ + val = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); + +#ifdef CONFIG_USER_ONLY + s = &gdbserver_state; + memset (s, 0, sizeof (GDBState)); +#else + s = qemu_mallocz(sizeof(GDBState)); + if (!s) { + close(fd); + return; + } +#endif + s->env = first_cpu; /* XXX: allow to change CPU */ + s->fd = fd; + + fcntl(fd, F_SETFL, O_NONBLOCK); + +#ifndef CONFIG_USER_ONLY + /* stop the VM */ + vm_stop(EXCP_INTERRUPT); + + /* start handling I/O */ + qemu_set_fd_handler(s->fd, gdb_read, NULL, s); + /* when the VM is stopped, the following callback is called */ + qemu_add_vm_stop_handler(gdb_vm_stopped, s); +#endif +} + +static int gdbserver_open(int port) +{ + struct sockaddr_in sockaddr; + int fd, val, ret; + + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + + /* allow fast reuse */ + val = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(port); + sockaddr.sin_addr.s_addr = 0; + ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); + if (ret < 0) { + perror("bind"); + return -1; + } + ret = listen(fd, 0); + if (ret < 0) { + perror("listen"); + return -1; + } +#ifndef CONFIG_USER_ONLY + fcntl(fd, F_SETFL, O_NONBLOCK); +#endif + return fd; +} + +int gdbserver_start(int port) +{ + gdbserver_fd = gdbserver_open(port); + if (gdbserver_fd < 0) + return -1; + /* accept connections */ +#ifdef CONFIG_USER_ONLY + gdb_accept (NULL); +#else + qemu_set_fd_handler(gdbserver_fd, gdb_accept, NULL, NULL); +#endif + return 0; +} diff --git a/tools/ioemu/gdbstub.h b/tools/ioemu/gdbstub.h new file mode 100644 index 0000000000..7b42596f15 --- /dev/null +++ b/tools/ioemu/gdbstub.h @@ -0,0 +1,12 @@ +#ifndef GDBSTUB_H +#define GDBSTUB_H + +#define DEFAULT_GDBSTUB_PORT 1234 + +#ifdef CONFIG_USER_ONLY +int gdb_handlesig (CPUState *, int); +void gdb_exit(CPUState *, int); +#endif +int gdbserver_start(int); + +#endif diff --git a/tools/ioemu/hw/CVS/Entries b/tools/ioemu/hw/CVS/Entries new file mode 100644 index 0000000000..aa66f2b34f --- /dev/null +++ b/tools/ioemu/hw/CVS/Entries @@ -0,0 +1,72 @@ +/adb.c/1.6/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/adlib.c/1.5/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/apic.c/1.8/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/arm_boot.c/1.1/Thu Apr 27 23:15:07 2006//Trelease_0_8_1 +/arm_pic.c/1.1/Sun Apr 9 01:32:52 2006//Trelease_0_8_1 +/arm_pic.h/1.1/Sun Apr 9 01:32:52 2006//Trelease_0_8_1 +/arm_timer.c/1.1/Sun Apr 9 01:32:52 2006//Trelease_0_8_1 +/cirrus_vga.c/1.21/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/cirrus_vga_rop.h/1.1/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/cirrus_vga_rop2.h/1.7/Thu May 25 12:52:49 2006//Trelease_0_8_1 +/cuda.c/1.10/Thu May 25 12:38:51 2006//Trelease_0_8_1 +/dma.c/1.14/Thu May 25 18:22:32 2006//Trelease_0_8_1 +/es1370.c/1.4/Sun Nov 20 16:20:39 2005//Trelease_0_8_1 +/esp.c/1.6/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/fdc.c/1.18/Wed May 24 10:40:13 2006//Trelease_0_8_1 +/fmopl.c/1.1/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/fmopl.h/1.1/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/heathrow_pic.c/1.2/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/i8254.c/1.8/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/i8259.c/1.18/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/ide.c/1.42/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/integratorcp.c/1.9/Thu Apr 27 23:15:07 2006//Trelease_0_8_1 +/iommu.c/1.6/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/lance.c/1.7/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/m48t59.c/1.7/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/m48t59.h/1.5/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/mc146818rtc.c/1.6/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/mips_r4k.c/1.16/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/ne2000.c/1.19/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/openpic.c/1.9/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/parallel.c/1.4/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/pc.c/1.53/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/pci.c/1.24/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/pckbd.c/1.15/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/pcspk.c/1.1/Mon Apr 24 21:58:30 2006//Trelease_0_8_1 +/pl011.c/1.1/Sun Apr 9 01:32:52 2006//Trelease_0_8_1 +/pl050.c/1.1/Sun Apr 9 01:32:52 2006//Trelease_0_8_1 +/pl080.c/1.1/Sun Apr 9 01:32:52 2006//Trelease_0_8_1 +/pl110.c/1.6/Tue Apr 18 19:02:59 2006//Trelease_0_8_1 +/pl110_template.h/1.2/Sun Feb 19 12:31:32 2006//Trelease_0_8_1 +/pl190.c/1.1/Sun Apr 9 01:32:52 2006//Trelease_0_8_1 +/ppc.c/1.9/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/ppc_chrp.c/1.21/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/ppc_prep.c/1.26/Thu May 25 18:22:33 2006//Trelease_0_8_1 +/ps2.c/1.4/Wed Apr 12 21:09:07 2006//Trelease_0_8_1 +/rtl8139.c/1.1/Sun Feb 5 04:14:41 2006//Trelease_0_8_1 +/sb16.c/1.19/Thu May 25 18:22:34 2006//Trelease_0_8_1 +/serial.c/1.12/Thu May 25 18:22:34 2006//Trelease_0_8_1 +/sh7750.c/1.1/Thu Apr 27 21:32:09 2006//Trelease_0_8_1 +/sh7750_regnames.c/1.1/Thu Apr 27 21:32:09 2006//Trelease_0_8_1 +/sh7750_regnames.h/1.1/Thu Apr 27 21:32:09 2006//Trelease_0_8_1 +/sh7750_regs.h/1.1/Thu Apr 27 21:32:09 2006//Trelease_0_8_1 +/shix.c/1.1/Thu Apr 27 21:32:09 2006//Trelease_0_8_1 +/slavio_intctl.c/1.6/Thu May 25 18:22:35 2006//Trelease_0_8_1 +/slavio_misc.c/1.3/Thu May 25 18:22:35 2006//Trelease_0_8_1 +/slavio_serial.c/1.6/Thu May 25 18:22:35 2006//Trelease_0_8_1 +/slavio_timer.c/1.3/Thu May 25 18:22:35 2006//Trelease_0_8_1 +/smc91c111.c/1.3/Sat Feb 4 22:15:28 2006//Trelease_0_8_1 +/sun4m.c/1.16/Thu May 25 18:22:35 2006//Trelease_0_8_1 +/sun4u.c/1.8/Thu May 25 18:22:35 2006//Trelease_0_8_1 +/tc58128.c/1.1/Thu Apr 27 21:32:09 2006//Trelease_0_8_1 +/tcx.c/1.7/Thu May 25 18:22:35 2006//Trelease_0_8_1 +/usb-hid.c/1.2/Wed Apr 12 21:09:07 2006//Trelease_0_8_1 +/usb-hub.c/1.3/Sun Apr 30 21:53:59 2006//Trelease_0_8_1 +/usb-uhci.c/1.8/Tue Apr 25 21:01:19 2006//Trelease_0_8_1 +/usb.c/1.6/Mon Apr 24 21:18:20 2006//Trelease_0_8_1 +/usb.h/1.4/Wed Apr 12 21:09:07 2006//Trelease_0_8_1 +/versatilepb.c/1.2/Thu Apr 27 23:15:07 2006//Trelease_0_8_1 +/vga.c/1.42/Thu May 25 18:22:36 2006//Trelease_0_8_1 +/vga_int.h/1.6/Thu May 25 18:20:53 2006//Trelease_0_8_1 +/vga_template.h/1.11/Wed May 17 14:47:01 2006//Trelease_0_8_1 +D diff --git a/tools/ioemu/hw/CVS/Repository b/tools/ioemu/hw/CVS/Repository new file mode 100644 index 0000000000..e65456b391 --- /dev/null +++ b/tools/ioemu/hw/CVS/Repository @@ -0,0 +1 @@ +qemu/hw diff --git a/tools/ioemu/hw/CVS/Root b/tools/ioemu/hw/CVS/Root new file mode 100644 index 0000000000..e8a9815e6a --- /dev/null +++ b/tools/ioemu/hw/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.savannah.nongnu.org:/sources/qemu diff --git a/tools/ioemu/hw/CVS/Tag b/tools/ioemu/hw/CVS/Tag new file mode 100644 index 0000000000..31c251a4bf --- /dev/null +++ b/tools/ioemu/hw/CVS/Tag @@ -0,0 +1 @@ +Nrelease_0_8_1 diff --git a/tools/ioemu/hw/adb.c b/tools/ioemu/hw/adb.c new file mode 100644 index 0000000000..8e08cb143a --- /dev/null +++ b/tools/ioemu/hw/adb.c @@ -0,0 +1,410 @@ +/* + * QEMU ADB support + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* ADB commands */ +#define ADB_BUSRESET 0x00 +#define ADB_FLUSH 0x01 +#define ADB_WRITEREG 0x08 +#define ADB_READREG 0x0c + +/* ADB device commands */ +#define ADB_CMD_SELF_TEST 0xff +#define ADB_CMD_CHANGE_ID 0xfe +#define ADB_CMD_CHANGE_ID_AND_ACT 0xfd +#define ADB_CMD_CHANGE_ID_AND_ENABLE 0x00 + +/* ADB default device IDs (upper 4 bits of ADB command byte) */ +#define ADB_DONGLE 1 +#define ADB_KEYBOARD 2 +#define ADB_MOUSE 3 +#define ADB_TABLET 4 +#define ADB_MODEM 5 +#define ADB_MISC 7 + +/* error codes */ +#define ADB_RET_NOTPRESENT (-2) + +int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) +{ + ADBDevice *d; + int devaddr, cmd, i; + + cmd = buf[0] & 0xf; + if (cmd == ADB_BUSRESET) { + for(i = 0; i < s->nb_devices; i++) { + d = &s->devices[i]; + if (d->devreset) { + d->devreset(d); + } + } + return 0; + } + devaddr = buf[0] >> 4; + for(i = 0; i < s->nb_devices; i++) { + d = &s->devices[i]; + if (d->devaddr == devaddr) { + return d->devreq(d, obuf, buf, len); + } + } + return ADB_RET_NOTPRESENT; +} + +/* XXX: move that to cuda ? */ +int adb_poll(ADBBusState *s, uint8_t *obuf) +{ + ADBDevice *d; + int olen, i; + uint8_t buf[1]; + + olen = 0; + for(i = 0; i < s->nb_devices; i++) { + if (s->poll_index >= s->nb_devices) + s->poll_index = 0; + d = &s->devices[s->poll_index]; + buf[0] = ADB_READREG | (d->devaddr << 4); + olen = adb_request(s, obuf + 1, buf, 1); + /* if there is data, we poll again the same device */ + if (olen > 0) { + obuf[0] = buf[0]; + olen++; + break; + } + s->poll_index++; + } + return olen; +} + +ADBDevice *adb_register_device(ADBBusState *s, int devaddr, + ADBDeviceRequest *devreq, + ADBDeviceReset *devreset, + void *opaque) +{ + ADBDevice *d; + if (s->nb_devices >= MAX_ADB_DEVICES) + return NULL; + d = &s->devices[s->nb_devices++]; + d->bus = s; + d->devaddr = devaddr; + d->devreq = devreq; + d->devreset = devreset; + d->opaque = opaque; + return d; +} + +/***************************************************************/ +/* Keyboard ADB device */ + +typedef struct KBDState { + uint8_t data[128]; + int rptr, wptr, count; +} KBDState; + +static const uint8_t pc_to_adb_keycode[256] = { + 0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48, + 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54, 0, 1, + 2, 3, 5, 4, 38, 40, 37, 41, 39, 50, 56, 42, 6, 7, 8, 9, + 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96, + 97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83, + 84, 85, 82, 65, 0, 0, 10,103,111, 0, 0,110, 81, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 94, 0, 93, 0, 0, 0, 0, 0, 0,104,102, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76,125, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 75, 0, 0,124, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,115, 62,116, 0, 59, 0, 60, 0,119, + 61,121,114,117, 0, 0, 0, 0, 0, 0, 0, 55,126, 0,127, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static void adb_kbd_put_keycode(void *opaque, int keycode) +{ + ADBDevice *d = opaque; + KBDState *s = d->opaque; + + if (s->count < sizeof(s->data)) { + s->data[s->wptr] = keycode; + if (++s->wptr == sizeof(s->data)) + s->wptr = 0; + s->count++; + } +} + +static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf) +{ + static int ext_keycode; + KBDState *s = d->opaque; + int adb_keycode, keycode; + int olen; + + olen = 0; + for(;;) { + if (s->count == 0) + break; + keycode = s->data[s->rptr]; + if (++s->rptr == sizeof(s->data)) + s->rptr = 0; + s->count--; + + if (keycode == 0xe0) { + ext_keycode = 1; + } else { + if (ext_keycode) + adb_keycode = pc_to_adb_keycode[keycode | 0x80]; + else + adb_keycode = pc_to_adb_keycode[keycode & 0x7f]; + obuf[0] = adb_keycode | (keycode & 0x80); + /* NOTE: could put a second keycode if needed */ + obuf[1] = 0xff; + olen = 2; + ext_keycode = 0; + break; + } + } + return olen; +} + +static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, + const uint8_t *buf, int len) +{ + KBDState *s = d->opaque; + int cmd, reg, olen; + + if ((buf[0] & 0x0f) == ADB_FLUSH) { + /* flush keyboard fifo */ + s->wptr = s->rptr = s->count = 0; + return 0; + } + + cmd = buf[0] & 0xc; + reg = buf[0] & 0x3; + olen = 0; + switch(cmd) { + case ADB_WRITEREG: + switch(reg) { + case 2: + /* LED status */ + break; + case 3: + switch(buf[2]) { + case ADB_CMD_SELF_TEST: + break; + case ADB_CMD_CHANGE_ID: + case ADB_CMD_CHANGE_ID_AND_ACT: + case ADB_CMD_CHANGE_ID_AND_ENABLE: + d->devaddr = buf[1] & 0xf; + break; + default: + /* XXX: check this */ + d->devaddr = buf[1] & 0xf; + d->handler = buf[2]; + break; + } + } + break; + case ADB_READREG: + switch(reg) { + case 0: + olen = adb_kbd_poll(d, obuf); + break; + case 1: + break; + case 2: + obuf[0] = 0x00; /* XXX: check this */ + obuf[1] = 0x07; /* led status */ + olen = 2; + break; + case 3: + obuf[0] = d->handler; + obuf[1] = d->devaddr; + olen = 2; + break; + } + break; + } + return olen; +} + +static int adb_kbd_reset(ADBDevice *d) +{ + KBDState *s = d->opaque; + + d->handler = 1; + d->devaddr = ADB_KEYBOARD; + memset(s, 0, sizeof(KBDState)); + + return 0; +} + +void adb_kbd_init(ADBBusState *bus) +{ + ADBDevice *d; + KBDState *s; + s = qemu_mallocz(sizeof(KBDState)); + d = adb_register_device(bus, ADB_KEYBOARD, adb_kbd_request, + adb_kbd_reset, s); + adb_kbd_reset(d); + qemu_add_kbd_event_handler(adb_kbd_put_keycode, d); +} + +/***************************************************************/ +/* Mouse ADB device */ + +typedef struct MouseState { + int buttons_state, last_buttons_state; + int dx, dy, dz; +} MouseState; + +static void adb_mouse_event(void *opaque, + int dx1, int dy1, int dz1, int buttons_state) +{ + ADBDevice *d = opaque; + MouseState *s = d->opaque; + + s->dx += dx1; + s->dy += dy1; + s->dz += dz1; + s->buttons_state = buttons_state; +} + + +static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf) +{ + MouseState *s = d->opaque; + int dx, dy; + + if (s->last_buttons_state == s->buttons_state && + s->dx == 0 && s->dy == 0) + return 0; + + dx = s->dx; + if (dx < -63) + dx = -63; + else if (dx > 63) + dx = 63; + + dy = s->dy; + if (dy < -63) + dy = -63; + else if (dy > 63) + dy = 63; + + s->dx -= dx; + s->dy -= dy; + s->last_buttons_state = s->buttons_state; + + dx &= 0x7f; + dy &= 0x7f; + + if (!(s->buttons_state & MOUSE_EVENT_LBUTTON)) + dy |= 0x80; + if (!(s->buttons_state & MOUSE_EVENT_RBUTTON)) + dx |= 0x80; + + obuf[0] = dy; + obuf[1] = dx; + return 2; +} + +static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, + const uint8_t *buf, int len) +{ + MouseState *s = d->opaque; + int cmd, reg, olen; + + if ((buf[0] & 0x0f) == ADB_FLUSH) { + /* flush mouse fifo */ + s->buttons_state = s->last_buttons_state; + s->dx = 0; + s->dy = 0; + s->dz = 0; + return 0; + } + + cmd = buf[0] & 0xc; + reg = buf[0] & 0x3; + olen = 0; + switch(cmd) { + case ADB_WRITEREG: + switch(reg) { + case 2: + break; + case 3: + switch(buf[2]) { + case ADB_CMD_SELF_TEST: + break; + case ADB_CMD_CHANGE_ID: + case ADB_CMD_CHANGE_ID_AND_ACT: + case ADB_CMD_CHANGE_ID_AND_ENABLE: + d->devaddr = buf[1] & 0xf; + break; + default: + /* XXX: check this */ + d->devaddr = buf[1] & 0xf; + break; + } + } + break; + case ADB_READREG: + switch(reg) { + case 0: + olen = adb_mouse_poll(d, obuf); + break; + case 1: + break; + case 3: + obuf[0] = d->handler; + obuf[1] = d->devaddr; + olen = 2; + break; + } + break; + } + return olen; +} + +static int adb_mouse_reset(ADBDevice *d) +{ + MouseState *s = d->opaque; + + d->handler = 2; + d->devaddr = ADB_MOUSE; + memset(s, 0, sizeof(MouseState)); + + return 0; +} + +void adb_mouse_init(ADBBusState *bus) +{ + ADBDevice *d; + MouseState *s; + + s = qemu_mallocz(sizeof(MouseState)); + d = adb_register_device(bus, ADB_MOUSE, adb_mouse_request, + adb_mouse_reset, s); + adb_mouse_reset(d); + qemu_add_mouse_event_handler(adb_mouse_event, d, 0); +} diff --git a/tools/ioemu/hw/adlib.c b/tools/ioemu/hw/adlib.c new file mode 100644 index 0000000000..f482d1fa84 --- /dev/null +++ b/tools/ioemu/hw/adlib.c @@ -0,0 +1,341 @@ +/* + * QEMU Proxy for OPL2/3 emulation by MAME team + * + * Copyright (c) 2004-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include "vl.h" + +#define ADLIB_KILL_TIMERS 1 + +#define dolog(...) AUD_log ("adlib", __VA_ARGS__) +#ifdef DEBUG +#define ldebug(...) dolog (__VA_ARGS__) +#else +#define ldebug(...) +#endif + +#ifdef HAS_YMF262 +#include "ymf262.h" +void YMF262UpdateOneQEMU (int which, INT16 *dst, int length); +#define SHIFT 2 +#else +#include "fmopl.h" +#define SHIFT 1 +#endif + +#define IO_READ_PROTO(name) \ + uint32_t name (void *opaque, uint32_t nport) +#define IO_WRITE_PROTO(name) \ + void name (void *opaque, uint32_t nport, uint32_t val) + +static struct { + int port; + int freq; +} conf = {0x220, 44100}; + +typedef struct { + QEMUSoundCard card; + int ticking[2]; + int enabled; + int active; + int bufpos; +#ifdef DEBUG + int64_t exp[2]; +#endif + int16_t *mixbuf; + uint64_t dexp[2]; + SWVoiceOut *voice; + int left, pos, samples; + QEMUAudioTimeStamp ats; +#ifndef HAS_YMF262 + FM_OPL *opl; +#endif +} AdlibState; + +static AdlibState glob_adlib; + +static void adlib_stop_opl_timer (AdlibState *s, size_t n) +{ +#ifdef HAS_YMF262 + YMF262TimerOver (0, n); +#else + OPLTimerOver (s->opl, n); +#endif + s->ticking[n] = 0; +} + +static void adlib_kill_timers (AdlibState *s) +{ + size_t i; + + for (i = 0; i < 2; ++i) { + if (s->ticking[i]) { + uint64_t delta; + + delta = AUD_get_elapsed_usec_out (s->voice, &s->ats); + ldebug ( + "delta = %f dexp = %f expired => %d\n", + delta / 1000000.0, + s->dexp[i] / 1000000.0, + delta >= s->dexp[i] + ); + if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) { + adlib_stop_opl_timer (s, i); + AUD_init_time_stamp_out (s->voice, &s->ats); + } + } + } +} + +static IO_WRITE_PROTO(adlib_write) +{ + AdlibState *s = opaque; + int a = nport & 3; + int status; + + s->active = 1; + AUD_set_active_out (s->voice, 1); + + adlib_kill_timers (s); + +#ifdef HAS_YMF262 + status = YMF262Write (0, a, val); +#else + status = OPLWrite (s->opl, a, val); +#endif +} + +static IO_READ_PROTO(adlib_read) +{ + AdlibState *s = opaque; + uint8_t data; + int a = nport & 3; + + adlib_kill_timers (s); + +#ifdef HAS_YMF262 + data = YMF262Read (0, a); +#else + data = OPLRead (s->opl, a); +#endif + return data; +} + +static void timer_handler (int c, double interval_Sec) +{ + AdlibState *s = &glob_adlib; + unsigned n = c & 1; +#ifdef DEBUG + double interval; + int64_t exp; +#endif + + if (interval_Sec == 0.0) { + s->ticking[n] = 0; + return; + } + + s->ticking[n] = 1; +#ifdef DEBUG + interval = ticks_per_sec * interval_Sec; + exp = qemu_get_clock (vm_clock) + interval; + s->exp[n] = exp; +#endif + + s->dexp[n] = interval_Sec * 1000000.0; + AUD_init_time_stamp_out (s->voice, &s->ats); +} + +static int write_audio (AdlibState *s, int samples) +{ + int net = 0; + int pos = s->pos; + + while (samples) { + int nbytes, wbytes, wsampl; + + nbytes = samples << SHIFT; + wbytes = AUD_write ( + s->voice, + s->mixbuf + (pos << (SHIFT - 1)), + nbytes + ); + + if (wbytes) { + wsampl = wbytes >> SHIFT; + + samples -= wsampl; + pos = (pos + wsampl) % s->samples; + + net += wsampl; + } + else { + break; + } + } + + return net; +} + +static void adlib_callback (void *opaque, int free) +{ + AdlibState *s = opaque; + int samples, net = 0, to_play, written; + + samples = free >> SHIFT; + if (!(s->active && s->enabled) || !samples) { + return; + } + + to_play = audio_MIN (s->left, samples); + while (to_play) { + written = write_audio (s, to_play); + + if (written) { + s->left -= written; + samples -= written; + to_play -= written; + s->pos = (s->pos + written) % s->samples; + } + else { + return; + } + } + + samples = audio_MIN (samples, s->samples - s->pos); + if (!samples) { + return; + } + +#ifdef HAS_YMF262 + YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples); +#else + YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples); +#endif + + while (samples) { + written = write_audio (s, samples); + + if (written) { + net += written; + samples -= written; + s->pos = (s->pos + written) % s->samples; + } + else { + s->left = samples; + return; + } + } +} + +static void Adlib_fini (AdlibState *s) +{ +#ifdef HAS_YMF262 + YMF262Shutdown (); +#else + if (s->opl) { + OPLDestroy (s->opl); + s->opl = NULL; + } +#endif + + if (s->mixbuf) { + qemu_free (s->mixbuf); + } + + s->active = 0; + s->enabled = 0; + AUD_remove_card (&s->card); +} + +int Adlib_init (AudioState *audio) +{ + AdlibState *s = &glob_adlib; + audsettings_t as; + + if (!audio) { + dolog ("No audio state\n"); + return -1; + } + +#ifdef HAS_YMF262 + if (YMF262Init (1, 14318180, conf.freq)) { + dolog ("YMF262Init %d failed\n", conf.freq); + return -1; + } + else { + YMF262SetTimerHandler (0, timer_handler, 0); + s->enabled = 1; + } +#else + s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq); + if (!s->opl) { + dolog ("OPLCreate %d failed\n", conf.freq); + return -1; + } + else { + OPLSetTimerHandler (s->opl, timer_handler, 0); + s->enabled = 1; + } +#endif + + as.freq = conf.freq; + as.nchannels = SHIFT; + as.fmt = AUD_FMT_S16; + + AUD_register_card (audio, "adlib", &s->card); + + s->voice = AUD_open_out ( + &s->card, + s->voice, + "adlib", + s, + adlib_callback, + &as, + 0 /* XXX: little endian? */ + ); + if (!s->voice) { + Adlib_fini (s); + return -1; + } + + s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT; + s->mixbuf = qemu_mallocz (s->samples << SHIFT); + + if (!s->mixbuf) { + dolog ("Could not allocate mixing buffer, %d samples (each %d bytes)\n", + s->samples, 1 << SHIFT); + Adlib_fini (s); + return -1; + } + + register_ioport_read (0x388, 4, 1, adlib_read, s); + register_ioport_write (0x388, 4, 1, adlib_write, s); + + register_ioport_read (conf.port, 4, 1, adlib_read, s); + register_ioport_write (conf.port, 4, 1, adlib_write, s); + + register_ioport_read (conf.port + 8, 2, 1, adlib_read, s); + register_ioport_write (conf.port + 8, 2, 1, adlib_write, s); + + return 0; +} diff --git a/tools/ioemu/hw/apic.c b/tools/ioemu/hw/apic.c new file mode 100644 index 0000000000..65f96a5b76 --- /dev/null +++ b/tools/ioemu/hw/apic.c @@ -0,0 +1,1042 @@ +/* + * APIC support + * + * Copyright (c) 2004-2005 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "vl.h" + +//#define DEBUG_APIC +//#define DEBUG_IOAPIC + +/* APIC Local Vector Table */ +#define APIC_LVT_TIMER 0 +#define APIC_LVT_THERMAL 1 +#define APIC_LVT_PERFORM 2 +#define APIC_LVT_LINT0 3 +#define APIC_LVT_LINT1 4 +#define APIC_LVT_ERROR 5 +#define APIC_LVT_NB 6 + +/* APIC delivery modes */ +#define APIC_DM_FIXED 0 +#define APIC_DM_LOWPRI 1 +#define APIC_DM_SMI 2 +#define APIC_DM_NMI 4 +#define APIC_DM_INIT 5 +#define APIC_DM_SIPI 6 +#define APIC_DM_EXTINT 7 + +/* APIC destination mode */ +#define APIC_DESTMODE_FLAT 0xf +#define APIC_DESTMODE_CLUSTER 1 + +#define APIC_TRIGGER_EDGE 0 +#define APIC_TRIGGER_LEVEL 1 + +#define APIC_LVT_TIMER_PERIODIC (1<<17) +#define APIC_LVT_MASKED (1<<16) +#define APIC_LVT_LEVEL_TRIGGER (1<<15) +#define APIC_LVT_REMOTE_IRR (1<<14) +#define APIC_INPUT_POLARITY (1<<13) +#define APIC_SEND_PENDING (1<<12) + +#define IOAPIC_NUM_PINS 0x18 + +#define ESR_ILLEGAL_ADDRESS (1 << 7) + +#define APIC_SV_ENABLE (1 << 8) + +#define MAX_APICS 255 +#define MAX_APIC_WORDS 8 + +typedef struct APICState { + CPUState *cpu_env; + uint32_t apicbase; + uint8_t id; + uint8_t arb_id; + uint8_t tpr; + uint32_t spurious_vec; + uint8_t log_dest; + uint8_t dest_mode; + uint32_t isr[8]; /* in service register */ + uint32_t tmr[8]; /* trigger mode register */ + uint32_t irr[8]; /* interrupt request register */ + uint32_t lvt[APIC_LVT_NB]; + uint32_t esr; /* error register */ + uint32_t icr[2]; + + uint32_t divide_conf; + int count_shift; + uint32_t initial_count; + int64_t initial_count_load_time, next_time; + QEMUTimer *timer; +} APICState; + +struct IOAPICState { + uint8_t id; + uint8_t ioregsel; + + uint32_t irr; + uint64_t ioredtbl[IOAPIC_NUM_PINS]; +}; + +static int apic_io_memory; +static APICState *local_apics[MAX_APICS + 1]; +static int last_apic_id = 0; + +static void apic_init_ipi(APICState *s); +static void apic_set_irq(APICState *s, int vector_num, int trigger_mode); +static void apic_update_irq(APICState *s); + +/* Find first bit starting from msb. Return 0 if value = 0 */ +static int fls_bit(uint32_t value) +{ + unsigned int ret = 0; + +#if defined(HOST_I386) + __asm__ __volatile__ ("bsr %1, %0\n" : "+r" (ret) : "rm" (value)); + return ret; +#else + if (value > 0xffff) + value >>= 16, ret = 16; + if (value > 0xff) + value >>= 8, ret += 8; + if (value > 0xf) + value >>= 4, ret += 4; + if (value > 0x3) + value >>= 2, ret += 2; + return ret + (value >> 1); +#endif +} + +/* Find first bit starting from lsb. Return 0 if value = 0 */ +static int ffs_bit(uint32_t value) +{ + unsigned int ret = 0; + +#if defined(HOST_I386) + __asm__ __volatile__ ("bsf %1, %0\n" : "+r" (ret) : "rm" (value)); + return ret; +#else + if (!value) + return 0; + if (!(value & 0xffff)) + value >>= 16, ret = 16; + if (!(value & 0xff)) + value >>= 8, ret += 8; + if (!(value & 0xf)) + value >>= 4, ret += 4; + if (!(value & 0x3)) + value >>= 2, ret += 2; + if (!(value & 0x1)) + ret++; + return ret; +#endif +} + +static inline void set_bit(uint32_t *tab, int index) +{ + int i, mask; + i = index >> 5; + mask = 1 << (index & 0x1f); + tab[i] |= mask; +} + +static inline void reset_bit(uint32_t *tab, int index) +{ + int i, mask; + i = index >> 5; + mask = 1 << (index & 0x1f); + tab[i] &= ~mask; +} + +#define foreach_apic(apic, deliver_bitmask, code) \ +{\ + int __i, __j, __mask;\ + for(__i = 0; __i < MAX_APIC_WORDS; __i++) {\ + __mask = deliver_bitmask[__i];\ + if (__mask) {\ + for(__j = 0; __j < 32; __j++) {\ + if (__mask & (1 << __j)) {\ + apic = local_apics[__i * 32 + __j];\ + if (apic) {\ + code;\ + }\ + }\ + }\ + }\ + }\ +} + +static void apic_bus_deliver(const uint32_t *deliver_bitmask, + uint8_t delivery_mode, + uint8_t vector_num, uint8_t polarity, + uint8_t trigger_mode) +{ + APICState *apic_iter; + + switch (delivery_mode) { + case APIC_DM_LOWPRI: + /* XXX: search for focus processor, arbitration */ + { + int i, d; + d = -1; + for(i = 0; i < MAX_APIC_WORDS; i++) { + if (deliver_bitmask[i]) { + d = i * 32 + ffs_bit(deliver_bitmask[i]); + break; + } + } + if (d >= 0) { + apic_iter = local_apics[d]; + if (apic_iter) { + apic_set_irq(apic_iter, vector_num, trigger_mode); + } + } + } + return; + + case APIC_DM_FIXED: + break; + + case APIC_DM_SMI: + case APIC_DM_NMI: + break; + + case APIC_DM_INIT: + /* normal INIT IPI sent to processors */ + foreach_apic(apic_iter, deliver_bitmask, + apic_init_ipi(apic_iter) ); + return; + + case APIC_DM_EXTINT: + /* handled in I/O APIC code */ + break; + + default: + return; + } + + foreach_apic(apic_iter, deliver_bitmask, + apic_set_irq(apic_iter, vector_num, trigger_mode) ); +} + +void cpu_set_apic_base(CPUState *env, uint64_t val) +{ + APICState *s = env->apic_state; +#ifdef DEBUG_APIC + printf("cpu_set_apic_base: %016llx\n", val); +#endif + s->apicbase = (val & 0xfffff000) | + (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE)); + /* if disabled, cannot be enabled again */ + if (!(val & MSR_IA32_APICBASE_ENABLE)) { + s->apicbase &= ~MSR_IA32_APICBASE_ENABLE; + env->cpuid_features &= ~CPUID_APIC; + s->spurious_vec &= ~APIC_SV_ENABLE; + } +} + +uint64_t cpu_get_apic_base(CPUState *env) +{ + APICState *s = env->apic_state; +#ifdef DEBUG_APIC + printf("cpu_get_apic_base: %016llx\n", (uint64_t)s->apicbase); +#endif + return s->apicbase; +} + +void cpu_set_apic_tpr(CPUX86State *env, uint8_t val) +{ + APICState *s = env->apic_state; + s->tpr = (val & 0x0f) << 4; + apic_update_irq(s); +} + +uint8_t cpu_get_apic_tpr(CPUX86State *env) +{ + APICState *s = env->apic_state; + return s->tpr >> 4; +} + +/* return -1 if no bit is set */ +static int get_highest_priority_int(uint32_t *tab) +{ + int i; + for(i = 7; i >= 0; i--) { + if (tab[i] != 0) { + return i * 32 + fls_bit(tab[i]); + } + } + return -1; +} + +static int apic_get_ppr(APICState *s) +{ + int tpr, isrv, ppr; + + tpr = (s->tpr >> 4); + isrv = get_highest_priority_int(s->isr); + if (isrv < 0) + isrv = 0; + isrv >>= 4; + if (tpr >= isrv) + ppr = s->tpr; + else + ppr = isrv << 4; + return ppr; +} + +static int apic_get_arb_pri(APICState *s) +{ + /* XXX: arbitration */ + return 0; +} + +/* signal the CPU if an irq is pending */ +static void apic_update_irq(APICState *s) +{ + int irrv, ppr; + if (!(s->spurious_vec & APIC_SV_ENABLE)) + return; + irrv = get_highest_priority_int(s->irr); + if (irrv < 0) + return; + ppr = apic_get_ppr(s); + if (ppr && (irrv & 0xf0) <= (ppr & 0xf0)) + return; + cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); +} + +static void apic_set_irq(APICState *s, int vector_num, int trigger_mode) +{ + set_bit(s->irr, vector_num); + if (trigger_mode) + set_bit(s->tmr, vector_num); + else + reset_bit(s->tmr, vector_num); + apic_update_irq(s); +} + +static void apic_eoi(APICState *s) +{ + int isrv; + isrv = get_highest_priority_int(s->isr); + if (isrv < 0) + return; + reset_bit(s->isr, isrv); + /* XXX: send the EOI packet to the APIC bus to allow the I/O APIC to + set the remote IRR bit for level triggered interrupts. */ + apic_update_irq(s); +} + +static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask, + uint8_t dest, uint8_t dest_mode) +{ + APICState *apic_iter; + int i; + + if (dest_mode == 0) { + if (dest == 0xff) { + memset(deliver_bitmask, 0xff, MAX_APIC_WORDS * sizeof(uint32_t)); + } else { + memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t)); + set_bit(deliver_bitmask, dest); + } + } else { + /* XXX: cluster mode */ + memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t)); + for(i = 0; i < MAX_APICS; i++) { + apic_iter = local_apics[i]; + if (apic_iter) { + if (apic_iter->dest_mode == 0xf) { + if (dest & apic_iter->log_dest) + set_bit(deliver_bitmask, i); + } else if (apic_iter->dest_mode == 0x0) { + if ((dest & 0xf0) == (apic_iter->log_dest & 0xf0) && + (dest & apic_iter->log_dest & 0x0f)) { + set_bit(deliver_bitmask, i); + } + } + } + } + } +} + + +static void apic_init_ipi(APICState *s) +{ + int i; + + for(i = 0; i < APIC_LVT_NB; i++) + s->lvt[i] = 1 << 16; /* mask LVT */ + s->tpr = 0; + s->spurious_vec = 0xff; + s->log_dest = 0; + s->dest_mode = 0xf; + memset(s->isr, 0, sizeof(s->isr)); + memset(s->tmr, 0, sizeof(s->tmr)); + memset(s->irr, 0, sizeof(s->irr)); + memset(s->lvt, 0, sizeof(s->lvt)); + s->esr = 0; + memset(s->icr, 0, sizeof(s->icr)); + s->divide_conf = 0; + s->count_shift = 0; + s->initial_count = 0; + s->initial_count_load_time = 0; + s->next_time = 0; +} + +/* send a SIPI message to the CPU to start it */ +static void apic_startup(APICState *s, int vector_num) +{ + CPUState *env = s->cpu_env; + if (!(env->hflags & HF_HALTED_MASK)) + return; + env->eip = 0; + cpu_x86_load_seg_cache(env, R_CS, vector_num << 8, vector_num << 12, + 0xffff, 0); + env->hflags &= ~HF_HALTED_MASK; +} + +static void apic_deliver(APICState *s, uint8_t dest, uint8_t dest_mode, + uint8_t delivery_mode, uint8_t vector_num, + uint8_t polarity, uint8_t trigger_mode) +{ + uint32_t deliver_bitmask[MAX_APIC_WORDS]; + int dest_shorthand = (s->icr[0] >> 18) & 3; + APICState *apic_iter; + + switch (dest_shorthand) { + case 0: + apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode); + break; + case 1: + memset(deliver_bitmask, 0x00, sizeof(deliver_bitmask)); + set_bit(deliver_bitmask, s->id); + break; + case 2: + memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask)); + break; + case 3: + memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask)); + reset_bit(deliver_bitmask, s->id); + break; + } + + switch (delivery_mode) { + case APIC_DM_INIT: + { + int trig_mode = (s->icr[0] >> 15) & 1; + int level = (s->icr[0] >> 14) & 1; + if (level == 0 && trig_mode == 1) { + foreach_apic(apic_iter, deliver_bitmask, + apic_iter->arb_id = apic_iter->id ); + return; + } + } + break; + + case APIC_DM_SIPI: + foreach_apic(apic_iter, deliver_bitmask, + apic_startup(apic_iter, vector_num) ); + return; + } + + apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity, + trigger_mode); +} + +int apic_get_interrupt(CPUState *env) +{ + APICState *s = env->apic_state; + int intno; + + /* if the APIC is installed or enabled, we let the 8259 handle the + IRQs */ + if (!s) + return -1; + if (!(s->spurious_vec & APIC_SV_ENABLE)) + return -1; + + /* XXX: spurious IRQ handling */ + intno = get_highest_priority_int(s->irr); + if (intno < 0) + return -1; + reset_bit(s->irr, intno); + if (s->tpr && intno <= s->tpr) + return s->spurious_vec & 0xff; + set_bit(s->isr, intno); + apic_update_irq(s); + return intno; +} + +static uint32_t apic_get_current_count(APICState *s) +{ + int64_t d; + uint32_t val; + d = (qemu_get_clock(vm_clock) - s->initial_count_load_time) >> + s->count_shift; + if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { + /* periodic */ + val = s->initial_count - (d % ((uint64_t)s->initial_count + 1)); + } else { + if (d >= s->initial_count) + val = 0; + else + val = s->initial_count - d; + } + return val; +} + +static void apic_timer_update(APICState *s, int64_t current_time) +{ + int64_t next_time, d; + + if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) { + d = (current_time - s->initial_count_load_time) >> + s->count_shift; + if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { + d = ((d / ((uint64_t)s->initial_count + 1)) + 1) * ((uint64_t)s->initial_count + 1); + } else { + if (d >= s->initial_count) + goto no_timer; + d = (uint64_t)s->initial_count + 1; + } + next_time = s->initial_count_load_time + (d << s->count_shift); + qemu_mod_timer(s->timer, next_time); + s->next_time = next_time; + } else { + no_timer: + qemu_del_timer(s->timer); + } +} + +static void apic_timer(void *opaque) +{ + APICState *s = opaque; + + if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) { + apic_set_irq(s, s->lvt[APIC_LVT_TIMER] & 0xff, APIC_TRIGGER_EDGE); + } + apic_timer_update(s, s->next_time); +} + +static uint32_t apic_mem_readb(void *opaque, target_phys_addr_t addr) +{ + return 0; +} + +static uint32_t apic_mem_readw(void *opaque, target_phys_addr_t addr) +{ + return 0; +} + +static void apic_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ +} + +static void apic_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ +} + +static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr) +{ + CPUState *env; + APICState *s; + uint32_t val; + int index; + + env = cpu_single_env; + if (!env) + return 0; + s = env->apic_state; + + index = (addr >> 4) & 0xff; + switch(index) { + case 0x02: /* id */ + val = s->id << 24; + break; + case 0x03: /* version */ + val = 0x11 | ((APIC_LVT_NB - 1) << 16); /* version 0x11 */ + break; + case 0x08: + val = s->tpr; + break; + case 0x09: + val = apic_get_arb_pri(s); + break; + case 0x0a: + /* ppr */ + val = apic_get_ppr(s); + break; + case 0x0d: + val = s->log_dest << 24; + break; + case 0x0e: + val = s->dest_mode << 28; + break; + case 0x0f: + val = s->spurious_vec; + break; + case 0x10 ... 0x17: + val = s->isr[index & 7]; + break; + case 0x18 ... 0x1f: + val = s->tmr[index & 7]; + break; + case 0x20 ... 0x27: + val = s->irr[index & 7]; + break; + case 0x28: + val = s->esr; + break; + case 0x30: + case 0x31: + val = s->icr[index & 1]; + break; + case 0x32 ... 0x37: + val = s->lvt[index - 0x32]; + break; + case 0x38: + val = s->initial_count; + break; + case 0x39: + val = apic_get_current_count(s); + break; + case 0x3e: + val = s->divide_conf; + break; + default: + s->esr |= ESR_ILLEGAL_ADDRESS; + val = 0; + break; + } +#ifdef DEBUG_APIC + printf("APIC read: %08x = %08x\n", (uint32_t)addr, val); +#endif + return val; +} + +static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + CPUState *env; + APICState *s; + int index; + + env = cpu_single_env; + if (!env) + return; + s = env->apic_state; + +#ifdef DEBUG_APIC + printf("APIC write: %08x = %08x\n", (uint32_t)addr, val); +#endif + + index = (addr >> 4) & 0xff; + switch(index) { + case 0x02: + s->id = (val >> 24); + break; + case 0x03: + break; + case 0x08: + s->tpr = val; + apic_update_irq(s); + break; + case 0x09: + case 0x0a: + break; + case 0x0b: /* EOI */ + apic_eoi(s); + break; + case 0x0d: + s->log_dest = val >> 24; + break; + case 0x0e: + s->dest_mode = val >> 28; + break; + case 0x0f: + s->spurious_vec = val & 0x1ff; + apic_update_irq(s); + break; + case 0x10 ... 0x17: + case 0x18 ... 0x1f: + case 0x20 ... 0x27: + case 0x28: + break; + case 0x30: + s->icr[0] = val; + apic_deliver(s, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1, + (s->icr[0] >> 8) & 7, (s->icr[0] & 0xff), + (s->icr[0] >> 14) & 1, (s->icr[0] >> 15) & 1); + break; + case 0x31: + s->icr[1] = val; + break; + case 0x32 ... 0x37: + { + int n = index - 0x32; + s->lvt[n] = val; + if (n == APIC_LVT_TIMER) + apic_timer_update(s, qemu_get_clock(vm_clock)); + } + break; + case 0x38: + s->initial_count = val; + s->initial_count_load_time = qemu_get_clock(vm_clock); + apic_timer_update(s, s->initial_count_load_time); + break; + case 0x39: + break; + case 0x3e: + { + int v; + s->divide_conf = val & 0xb; + v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4); + s->count_shift = (v + 1) & 7; + } + break; + default: + s->esr |= ESR_ILLEGAL_ADDRESS; + break; + } +} + +static void apic_save(QEMUFile *f, void *opaque) +{ + APICState *s = opaque; + int i; + + qemu_put_be32s(f, &s->apicbase); + qemu_put_8s(f, &s->id); + qemu_put_8s(f, &s->arb_id); + qemu_put_8s(f, &s->tpr); + qemu_put_be32s(f, &s->spurious_vec); + qemu_put_8s(f, &s->log_dest); + qemu_put_8s(f, &s->dest_mode); + for (i = 0; i < 8; i++) { + qemu_put_be32s(f, &s->isr[i]); + qemu_put_be32s(f, &s->tmr[i]); + qemu_put_be32s(f, &s->irr[i]); + } + for (i = 0; i < APIC_LVT_NB; i++) { + qemu_put_be32s(f, &s->lvt[i]); + } + qemu_put_be32s(f, &s->esr); + qemu_put_be32s(f, &s->icr[0]); + qemu_put_be32s(f, &s->icr[1]); + qemu_put_be32s(f, &s->divide_conf); + qemu_put_be32s(f, &s->count_shift); + qemu_put_be32s(f, &s->initial_count); + qemu_put_be64s(f, &s->initial_count_load_time); + qemu_put_be64s(f, &s->next_time); +} + +static int apic_load(QEMUFile *f, void *opaque, int version_id) +{ + APICState *s = opaque; + int i; + + if (version_id != 1) + return -EINVAL; + + /* XXX: what if the base changes? (registered memory regions) */ + qemu_get_be32s(f, &s->apicbase); + qemu_get_8s(f, &s->id); + qemu_get_8s(f, &s->arb_id); + qemu_get_8s(f, &s->tpr); + qemu_get_be32s(f, &s->spurious_vec); + qemu_get_8s(f, &s->log_dest); + qemu_get_8s(f, &s->dest_mode); + for (i = 0; i < 8; i++) { + qemu_get_be32s(f, &s->isr[i]); + qemu_get_be32s(f, &s->tmr[i]); + qemu_get_be32s(f, &s->irr[i]); + } + for (i = 0; i < APIC_LVT_NB; i++) { + qemu_get_be32s(f, &s->lvt[i]); + } + qemu_get_be32s(f, &s->esr); + qemu_get_be32s(f, &s->icr[0]); + qemu_get_be32s(f, &s->icr[1]); + qemu_get_be32s(f, &s->divide_conf); + qemu_get_be32s(f, &s->count_shift); + qemu_get_be32s(f, &s->initial_count); + qemu_get_be64s(f, &s->initial_count_load_time); + qemu_get_be64s(f, &s->next_time); + return 0; +} + +static void apic_reset(void *opaque) +{ + APICState *s = opaque; + apic_init_ipi(s); +} + +static CPUReadMemoryFunc *apic_mem_read[3] = { + apic_mem_readb, + apic_mem_readw, + apic_mem_readl, +}; + +static CPUWriteMemoryFunc *apic_mem_write[3] = { + apic_mem_writeb, + apic_mem_writew, + apic_mem_writel, +}; + +int apic_init(CPUState *env) +{ + APICState *s; + + if (last_apic_id >= MAX_APICS) + return -1; + s = qemu_mallocz(sizeof(APICState)); + if (!s) + return -1; + env->apic_state = s; + apic_init_ipi(s); + s->id = last_apic_id++; + s->cpu_env = env; + s->apicbase = 0xfee00000 | + (s->id ? 0 : MSR_IA32_APICBASE_BSP) | MSR_IA32_APICBASE_ENABLE; + + /* XXX: mapping more APICs at the same memory location */ + if (apic_io_memory == 0) { + /* NOTE: the APIC is directly connected to the CPU - it is not + on the global memory bus. */ + apic_io_memory = cpu_register_io_memory(0, apic_mem_read, + apic_mem_write, NULL); + cpu_register_physical_memory(s->apicbase & ~0xfff, 0x1000, + apic_io_memory); + } + s->timer = qemu_new_timer(vm_clock, apic_timer, s); + + register_savevm("apic", 0, 1, apic_save, apic_load, s); + qemu_register_reset(apic_reset, s); + + local_apics[s->id] = s; + return 0; +} + +static void ioapic_service(IOAPICState *s) +{ + uint8_t i; + uint8_t trig_mode; + uint8_t vector; + uint8_t delivery_mode; + uint32_t mask; + uint64_t entry; + uint8_t dest; + uint8_t dest_mode; + uint8_t polarity; + uint32_t deliver_bitmask[MAX_APIC_WORDS]; + + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + mask = 1 << i; + if (s->irr & mask) { + entry = s->ioredtbl[i]; + if (!(entry & APIC_LVT_MASKED)) { + trig_mode = ((entry >> 15) & 1); + dest = entry >> 56; + dest_mode = (entry >> 11) & 1; + delivery_mode = (entry >> 8) & 7; + polarity = (entry >> 13) & 1; + if (trig_mode == APIC_TRIGGER_EDGE) + s->irr &= ~mask; + if (delivery_mode == APIC_DM_EXTINT) + vector = pic_read_irq(isa_pic); + else + vector = entry & 0xff; + + apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode); + apic_bus_deliver(deliver_bitmask, delivery_mode, + vector, polarity, trig_mode); + } + } + } +} + +void ioapic_set_irq(void *opaque, int vector, int level) +{ + IOAPICState *s = opaque; + + if (vector >= 0 && vector < IOAPIC_NUM_PINS) { + uint32_t mask = 1 << vector; + uint64_t entry = s->ioredtbl[vector]; + + if ((entry >> 15) & 1) { + /* level triggered */ + if (level) { + s->irr |= mask; + ioapic_service(s); + } else { + s->irr &= ~mask; + } + } else { + /* edge triggered */ + if (level) { + s->irr |= mask; + ioapic_service(s); + } + } + } +} + +static uint32_t ioapic_mem_readl(void *opaque, target_phys_addr_t addr) +{ + IOAPICState *s = opaque; + int index; + uint32_t val = 0; + + addr &= 0xff; + if (addr == 0x00) { + val = s->ioregsel; + } else if (addr == 0x10) { + switch (s->ioregsel) { + case 0x00: + val = s->id << 24; + break; + case 0x01: + val = 0x11 | ((IOAPIC_NUM_PINS - 1) << 16); /* version 0x11 */ + break; + case 0x02: + val = 0; + break; + default: + index = (s->ioregsel - 0x10) >> 1; + if (index >= 0 && index < IOAPIC_NUM_PINS) { + if (s->ioregsel & 1) + val = s->ioredtbl[index] >> 32; + else + val = s->ioredtbl[index] & 0xffffffff; + } + } +#ifdef DEBUG_IOAPIC + printf("I/O APIC read: %08x = %08x\n", s->ioregsel, val); +#endif + } + return val; +} + +static void ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + IOAPICState *s = opaque; + int index; + + addr &= 0xff; + if (addr == 0x00) { + s->ioregsel = val; + return; + } else if (addr == 0x10) { +#ifdef DEBUG_IOAPIC + printf("I/O APIC write: %08x = %08x\n", s->ioregsel, val); +#endif + switch (s->ioregsel) { + case 0x00: + s->id = (val >> 24) & 0xff; + return; + case 0x01: + case 0x02: + return; + default: + index = (s->ioregsel - 0x10) >> 1; + if (index >= 0 && index < IOAPIC_NUM_PINS) { + if (s->ioregsel & 1) { + s->ioredtbl[index] &= 0xffffffff; + s->ioredtbl[index] |= (uint64_t)val << 32; + } else { + s->ioredtbl[index] &= ~0xffffffffULL; + s->ioredtbl[index] |= val; + } + ioapic_service(s); + } + } + } +} + +static void ioapic_save(QEMUFile *f, void *opaque) +{ + IOAPICState *s = opaque; + int i; + + qemu_put_8s(f, &s->id); + qemu_put_8s(f, &s->ioregsel); + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + qemu_put_be64s(f, &s->ioredtbl[i]); + } +} + +static int ioapic_load(QEMUFile *f, void *opaque, int version_id) +{ + IOAPICState *s = opaque; + int i; + + if (version_id != 1) + return -EINVAL; + + qemu_get_8s(f, &s->id); + qemu_get_8s(f, &s->ioregsel); + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + qemu_get_be64s(f, &s->ioredtbl[i]); + } + return 0; +} + +static void ioapic_reset(void *opaque) +{ + IOAPICState *s = opaque; + int i; + + memset(s, 0, sizeof(*s)); + for(i = 0; i < IOAPIC_NUM_PINS; i++) + s->ioredtbl[i] = 1 << 16; /* mask LVT */ +} + +static CPUReadMemoryFunc *ioapic_mem_read[3] = { + ioapic_mem_readl, + ioapic_mem_readl, + ioapic_mem_readl, +}; + +static CPUWriteMemoryFunc *ioapic_mem_write[3] = { + ioapic_mem_writel, + ioapic_mem_writel, + ioapic_mem_writel, +}; + +IOAPICState *ioapic_init(void) +{ + IOAPICState *s; + int io_memory; + + s = qemu_mallocz(sizeof(IOAPICState)); + if (!s) + return NULL; + ioapic_reset(s); + s->id = last_apic_id++; + + io_memory = cpu_register_io_memory(0, ioapic_mem_read, + ioapic_mem_write, s); + cpu_register_physical_memory(0xfec00000, 0x1000, io_memory); + + register_savevm("ioapic", 0, 1, ioapic_save, ioapic_load, s); + qemu_register_reset(ioapic_reset, s); + + return s; +} diff --git a/tools/ioemu/hw/arm_boot.c b/tools/ioemu/hw/arm_boot.c new file mode 100644 index 0000000000..0e28d4a07a --- /dev/null +++ b/tools/ioemu/hw/arm_boot.c @@ -0,0 +1,105 @@ +/* + * ARM kernel loader. + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GPL. + */ + +#include "vl.h" + +#define KERNEL_ARGS_ADDR 0x100 +#define KERNEL_LOAD_ADDR 0x00010000 +#define INITRD_LOAD_ADDR 0x00800000 + +/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */ +static uint32_t bootloader[] = { + 0xe3a00000, /* mov r0, #0 */ + 0xe3a01000, /* mov r1, #0x?? */ + 0xe3811c00, /* orr r1, r1, #0x??00 */ + 0xe59f2000, /* ldr r2, [pc, #0] */ + 0xe59ff000, /* ldr pc, [pc, #0] */ + 0, /* Address of kernel args. Set by integratorcp_init. */ + 0 /* Kernel entry point. Set by integratorcp_init. */ +}; + +static void set_kernel_args(uint32_t ram_size, int initrd_size, + const char *kernel_cmdline) +{ + uint32_t *p; + + p = (uint32_t *)(phys_ram_base + KERNEL_ARGS_ADDR); + /* ATAG_CORE */ + stl_raw(p++, 5); + stl_raw(p++, 0x54410001); + stl_raw(p++, 1); + stl_raw(p++, 0x1000); + stl_raw(p++, 0); + /* ATAG_MEM */ + stl_raw(p++, 4); + stl_raw(p++, 0x54410002); + stl_raw(p++, ram_size); + stl_raw(p++, 0); + if (initrd_size) { + /* ATAG_INITRD2 */ + stl_raw(p++, 4); + stl_raw(p++, 0x54420005); + stl_raw(p++, INITRD_LOAD_ADDR); + stl_raw(p++, initrd_size); + } + if (kernel_cmdline && *kernel_cmdline) { + /* ATAG_CMDLINE */ + int cmdline_size; + + cmdline_size = strlen(kernel_cmdline); + memcpy (p + 2, kernel_cmdline, cmdline_size + 1); + cmdline_size = (cmdline_size >> 2) + 1; + stl_raw(p++, cmdline_size + 2); + stl_raw(p++, 0x54410009); + p += cmdline_size; + } + /* ATAG_END */ + stl_raw(p++, 0); + stl_raw(p++, 0); +} + +void arm_load_kernel(int ram_size, const char *kernel_filename, + const char *kernel_cmdline, const char *initrd_filename, + int board_id) +{ + int kernel_size; + int initrd_size; + int n; + + /* Load the kernel. */ + if (!kernel_filename) { + fprintf(stderr, "Kernel image must be specified\n"); + exit(1); + } + kernel_size = load_image(kernel_filename, + phys_ram_base + KERNEL_LOAD_ADDR); + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); + exit(1); + } + if (initrd_filename) { + initrd_size = load_image(initrd_filename, + phys_ram_base + INITRD_LOAD_ADDR); + if (initrd_size < 0) { + fprintf(stderr, "qemu: could not load initrd '%s'\n", + initrd_filename); + exit(1); + } + } else { + initrd_size = 0; + } + bootloader[1] |= board_id & 0xff; + bootloader[2] |= (board_id >> 8) & 0xff; + bootloader[5] = KERNEL_ARGS_ADDR; + bootloader[6] = KERNEL_LOAD_ADDR; + for (n = 0; n < sizeof(bootloader) / 4; n++) + stl_raw(phys_ram_base + (n * 4), bootloader[n]); + set_kernel_args(ram_size, initrd_size, kernel_cmdline); +} + diff --git a/tools/ioemu/hw/arm_pic.c b/tools/ioemu/hw/arm_pic.c new file mode 100644 index 0000000000..fbc2d67d0a --- /dev/null +++ b/tools/ioemu/hw/arm_pic.c @@ -0,0 +1,73 @@ +/* + * Generic ARM Programmable Interrupt Controller support. + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the LGPL + */ + +#include "vl.h" +#include "arm_pic.h" + +/* Stub functions for hardware that doesn't exist. */ +void pic_set_irq(int irq, int level) +{ + cpu_abort(cpu_single_env, "pic_set_irq"); +} + +void pic_info(void) +{ +} + +void irq_info(void) +{ +} + + +void pic_set_irq_new(void *opaque, int irq, int level) +{ + arm_pic_handler *p = (arm_pic_handler *)opaque; + /* Call the real handler. */ + (*p)(opaque, irq, level); +} + +/* Model the IRQ/FIQ CPU interrupt lines as a two input interrupt controller. + Input 0 is IRQ and input 1 is FIQ. */ +typedef struct +{ + arm_pic_handler handler; + CPUState *cpu_env; +} arm_pic_cpu_state; + +static void arm_pic_cpu_handler(void *opaque, int irq, int level) +{ + arm_pic_cpu_state *s = (arm_pic_cpu_state *)opaque; + switch (irq) { + case ARM_PIC_CPU_IRQ: + if (level) + cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); + else + cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); + break; + case ARM_PIC_CPU_FIQ: + if (level) + cpu_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ); + else + cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ); + break; + default: + cpu_abort(s->cpu_env, "arm_pic_cpu_handler: Bad interrput line %d\n", + irq); + } +} + +void *arm_pic_init_cpu(CPUState *env) +{ + arm_pic_cpu_state *s; + + s = (arm_pic_cpu_state *)malloc(sizeof(arm_pic_cpu_state)); + s->handler = arm_pic_cpu_handler; + s->cpu_env = env; + return s; +} diff --git a/tools/ioemu/hw/arm_pic.h b/tools/ioemu/hw/arm_pic.h new file mode 100644 index 0000000000..b29914985a --- /dev/null +++ b/tools/ioemu/hw/arm_pic.h @@ -0,0 +1,27 @@ +/* + * Generic ARM Programmable Interrupt Controller support. + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the LGPL. + * + * Arm hardware uses a wide variety of interrupt handling hardware. + * This provides a generic framework for connecting interrupt sources and + * inputs. + */ + +#ifndef ARM_INTERRUPT_H +#define ARM_INTERRUPT_H 1 + +/* The first element of an individual PIC state structures should + be a pointer to the handler routine. */ +typedef void (*arm_pic_handler)(void *opaque, int irq, int level); + +/* The CPU is also modeled as an interrupt controller. */ +#define ARM_PIC_CPU_IRQ 0 +#define ARM_PIC_CPU_FIQ 1 +void *arm_pic_init_cpu(CPUState *env); + +#endif /* !ARM_INTERRUPT_H */ + diff --git a/tools/ioemu/hw/arm_timer.c b/tools/ioemu/hw/arm_timer.c new file mode 100644 index 0000000000..a97d73e447 --- /dev/null +++ b/tools/ioemu/hw/arm_timer.c @@ -0,0 +1,383 @@ +/* + * ARM PrimeCell Timer modules. + * + * Copyright (c) 2005-2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GPL. + */ + +#include "vl.h" +#include "arm_pic.h" + +/* Common timer implementation. */ + +#define TIMER_CTRL_ONESHOT (1 << 0) +#define TIMER_CTRL_32BIT (1 << 1) +#define TIMER_CTRL_DIV1 (0 << 2) +#define TIMER_CTRL_DIV16 (1 << 2) +#define TIMER_CTRL_DIV256 (2 << 2) +#define TIMER_CTRL_IE (1 << 5) +#define TIMER_CTRL_PERIODIC (1 << 6) +#define TIMER_CTRL_ENABLE (1 << 7) + +typedef struct { + int64_t next_time; + int64_t expires; + int64_t loaded; + QEMUTimer *timer; + uint32_t control; + uint32_t count; + uint32_t limit; + int raw_freq; + int freq; + int int_level; + void *pic; + int irq; +} arm_timer_state; + +/* Calculate the new expiry time of the given timer. */ + +static void arm_timer_reload(arm_timer_state *s) +{ + int64_t delay; + + s->loaded = s->expires; + delay = muldiv64(s->count, ticks_per_sec, s->freq); + if (delay == 0) + delay = 1; + s->expires += delay; +} + +/* Check all active timers, and schedule the next timer interrupt. */ + +static void arm_timer_update(arm_timer_state *s, int64_t now) +{ + int64_t next; + + /* Ignore disabled timers. */ + if ((s->control & TIMER_CTRL_ENABLE) == 0) + return; + /* Ignore expired one-shot timers. */ + if (s->count == 0 && (s->control & TIMER_CTRL_ONESHOT)) + return; + if (s->expires - now <= 0) { + /* Timer has expired. */ + s->int_level = 1; + if (s->control & TIMER_CTRL_ONESHOT) { + /* One-shot. */ + s->count = 0; + } else { + if ((s->control & TIMER_CTRL_PERIODIC) == 0) { + /* Free running. */ + if (s->control & TIMER_CTRL_32BIT) + s->count = 0xffffffff; + else + s->count = 0xffff; + } else { + /* Periodic. */ + s->count = s->limit; + } + } + } + while (s->expires - now <= 0) { + arm_timer_reload(s); + } + /* Update interrupts. */ + if (s->int_level && (s->control & TIMER_CTRL_IE)) { + pic_set_irq_new(s->pic, s->irq, 1); + } else { + pic_set_irq_new(s->pic, s->irq, 0); + } + + next = now; + if (next - s->expires < 0) + next = s->expires; + + /* Schedule the next timer interrupt. */ + if (next == now) { + qemu_del_timer(s->timer); + s->next_time = 0; + } else if (next != s->next_time) { + qemu_mod_timer(s->timer, next); + s->next_time = next; + } +} + +/* Return the current value of the timer. */ +static uint32_t arm_timer_getcount(arm_timer_state *s, int64_t now) +{ + int64_t elapsed; + int64_t period; + + if (s->count == 0) + return 0; + if ((s->control & TIMER_CTRL_ENABLE) == 0) + return s->count; + elapsed = now - s->loaded; + period = s->expires - s->loaded; + /* If the timer should have expired then return 0. This can happen + when the host timer signal doesnt occur immediately. It's better to + have a timer appear to sit at zero for a while than have it wrap + around before the guest interrupt is raised. */ + /* ??? Could we trigger the interrupt here? */ + if (elapsed > period) + return 0; + /* We need to calculate count * elapsed / period without overfowing. + Scale both elapsed and period so they fit in a 32-bit int. */ + while (period != (int32_t)period) { + period >>= 1; + elapsed >>= 1; + } + return ((uint64_t)s->count * (uint64_t)(int32_t)elapsed) + / (int32_t)period; +} + +uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset) +{ + arm_timer_state *s = (arm_timer_state *)opaque; + + switch (offset >> 2) { + case 0: /* TimerLoad */ + case 6: /* TimerBGLoad */ + return s->limit; + case 1: /* TimerValue */ + return arm_timer_getcount(s, qemu_get_clock(vm_clock)); + case 2: /* TimerControl */ + return s->control; + case 4: /* TimerRIS */ + return s->int_level; + case 5: /* TimerMIS */ + if ((s->control & TIMER_CTRL_IE) == 0) + return 0; + return s->int_level; + default: + cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n", offset); + return 0; + } +} + +static void arm_timer_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + arm_timer_state *s = (arm_timer_state *)opaque; + int64_t now; + + now = qemu_get_clock(vm_clock); + switch (offset >> 2) { + case 0: /* TimerLoad */ + s->limit = value; + s->count = value; + s->expires = now; + arm_timer_reload(s); + break; + case 1: /* TimerValue */ + /* ??? Linux seems to want to write to this readonly register. + Ignore it. */ + break; + case 2: /* TimerControl */ + if (s->control & TIMER_CTRL_ENABLE) { + /* Pause the timer if it is running. This may cause some + inaccuracy dure to rounding, but avoids a whole lot of other + messyness. */ + s->count = arm_timer_getcount(s, now); + } + s->control = value; + s->freq = s->raw_freq; + /* ??? Need to recalculate expiry time after changing divisor. */ + switch ((value >> 2) & 3) { + case 1: s->freq >>= 4; break; + case 2: s->freq >>= 8; break; + } + if (s->control & TIMER_CTRL_ENABLE) { + /* Restart the timer if still enabled. */ + s->expires = now; + arm_timer_reload(s); + } + break; + case 3: /* TimerIntClr */ + s->int_level = 0; + break; + case 6: /* TimerBGLoad */ + s->limit = value; + break; + default: + cpu_abort (cpu_single_env, "arm_timer_write: Bad offset %x\n", offset); + } + arm_timer_update(s, now); +} + +static void arm_timer_tick(void *opaque) +{ + int64_t now; + + now = qemu_get_clock(vm_clock); + arm_timer_update((arm_timer_state *)opaque, now); +} + +static void *arm_timer_init(uint32_t freq, void *pic, int irq) +{ + arm_timer_state *s; + + s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state)); + s->pic = pic; + s->irq = irq; + s->raw_freq = s->freq = 1000000; + s->control = TIMER_CTRL_IE; + s->count = 0xffffffff; + + s->timer = qemu_new_timer(vm_clock, arm_timer_tick, s); + /* ??? Save/restore. */ + return s; +} + +/* ARM PrimeCell SP804 dual timer module. + Docs for this device don't seem to be publicly available. This + implementation is based on gueswork, the linux kernel sources and the + Integrator/CP timer modules. */ + +typedef struct { + /* Include a pseudo-PIC device to merge the two interrupt sources. */ + arm_pic_handler handler; + void *timer[2]; + int level[2]; + uint32_t base; + /* The output PIC device. */ + void *pic; + int irq; +} sp804_state; + +static void sp804_set_irq(void *opaque, int irq, int level) +{ + sp804_state *s = (sp804_state *)opaque; + + s->level[irq] = level; + pic_set_irq_new(s->pic, s->irq, s->level[0] || s->level[1]); +} + +static uint32_t sp804_read(void *opaque, target_phys_addr_t offset) +{ + sp804_state *s = (sp804_state *)opaque; + + /* ??? Don't know the PrimeCell ID for this device. */ + offset -= s->base; + if (offset < 0x20) { + return arm_timer_read(s->timer[0], offset); + } else { + return arm_timer_read(s->timer[1], offset - 0x20); + } +} + +static void sp804_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + sp804_state *s = (sp804_state *)opaque; + + offset -= s->base; + if (offset < 0x20) { + arm_timer_write(s->timer[0], offset, value); + } else { + arm_timer_write(s->timer[1], offset - 0x20, value); + } +} + +static CPUReadMemoryFunc *sp804_readfn[] = { + sp804_read, + sp804_read, + sp804_read +}; + +static CPUWriteMemoryFunc *sp804_writefn[] = { + sp804_write, + sp804_write, + sp804_write +}; + +void sp804_init(uint32_t base, void *pic, int irq) +{ + int iomemtype; + sp804_state *s; + + s = (sp804_state *)qemu_mallocz(sizeof(sp804_state)); + s->handler = sp804_set_irq; + s->base = base; + s->pic = pic; + s->irq = irq; + /* ??? The timers are actually configurable between 32kHz and 1MHz, but + we don't implement that. */ + s->timer[0] = arm_timer_init(1000000, s, 0); + s->timer[1] = arm_timer_init(1000000, s, 1); + iomemtype = cpu_register_io_memory(0, sp804_readfn, + sp804_writefn, s); + cpu_register_physical_memory(base, 0x00000fff, iomemtype); + /* ??? Save/restore. */ +} + + +/* Integrator/CP timer module. */ + +typedef struct { + void *timer[3]; + uint32_t base; +} icp_pit_state; + +static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset) +{ + icp_pit_state *s = (icp_pit_state *)opaque; + int n; + + /* ??? Don't know the PrimeCell ID for this device. */ + offset -= s->base; + n = offset >> 8; + if (n > 3) + cpu_abort(cpu_single_env, "sp804_read: Bad timer %d\n", n); + + return arm_timer_read(s->timer[n], offset & 0xff); +} + +static void icp_pit_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + icp_pit_state *s = (icp_pit_state *)opaque; + int n; + + offset -= s->base; + n = offset >> 8; + if (n > 3) + cpu_abort(cpu_single_env, "sp804_write: Bad timer %d\n", n); + + arm_timer_write(s->timer[n], offset & 0xff, value); +} + + +static CPUReadMemoryFunc *icp_pit_readfn[] = { + icp_pit_read, + icp_pit_read, + icp_pit_read +}; + +static CPUWriteMemoryFunc *icp_pit_writefn[] = { + icp_pit_write, + icp_pit_write, + icp_pit_write +}; + +void icp_pit_init(uint32_t base, void *pic, int irq) +{ + int iomemtype; + icp_pit_state *s; + + s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state)); + s->base = base; + /* Timer 0 runs at the system clock speed (40MHz). */ + s->timer[0] = arm_timer_init(40000000, pic, irq); + /* The other two timers run at 1MHz. */ + s->timer[1] = arm_timer_init(1000000, pic, irq + 1); + s->timer[2] = arm_timer_init(1000000, pic, irq + 2); + + iomemtype = cpu_register_io_memory(0, icp_pit_readfn, + icp_pit_writefn, s); + cpu_register_physical_memory(base, 0x00000fff, iomemtype); + /* ??? Save/restore. */ +} + diff --git a/tools/ioemu/hw/cirrus_vga.c b/tools/ioemu/hw/cirrus_vga.c new file mode 100644 index 0000000000..4a5631195c --- /dev/null +++ b/tools/ioemu/hw/cirrus_vga.c @@ -0,0 +1,3311 @@ +/* + * QEMU Cirrus CLGD 54xx VGA Emulator. + * + * Copyright (c) 2004 Fabrice Bellard + * Copyright (c) 2004 Makoto Suzuki (suzu) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* + * Reference: Finn Thogersons' VGADOC4b + * available at http://home.worldonline.dk/~finth/ + */ +#include "vl.h" +#include "vga_int.h" +#ifndef _WIN32 +#include +#endif + +/* + * TODO: + * - destination write mask support not complete (bits 5..7) + * - optimize linear mappings + * - optimize bitblt functions + */ + +//#define DEBUG_CIRRUS +//#define DEBUG_BITBLT + +/*************************************** + * + * definitions + * + ***************************************/ + +#define qemu_MIN(a,b) ((a) < (b) ? (a) : (b)) + +// ID +#define CIRRUS_ID_CLGD5422 (0x23<<2) +#define CIRRUS_ID_CLGD5426 (0x24<<2) +#define CIRRUS_ID_CLGD5424 (0x25<<2) +#define CIRRUS_ID_CLGD5428 (0x26<<2) +#define CIRRUS_ID_CLGD5430 (0x28<<2) +#define CIRRUS_ID_CLGD5434 (0x2A<<2) +#define CIRRUS_ID_CLGD5436 (0x2B<<2) +#define CIRRUS_ID_CLGD5446 (0x2E<<2) + +// sequencer 0x07 +#define CIRRUS_SR7_BPP_VGA 0x00 +#define CIRRUS_SR7_BPP_SVGA 0x01 +#define CIRRUS_SR7_BPP_MASK 0x0e +#define CIRRUS_SR7_BPP_8 0x00 +#define CIRRUS_SR7_BPP_16_DOUBLEVCLK 0x02 +#define CIRRUS_SR7_BPP_24 0x04 +#define CIRRUS_SR7_BPP_16 0x06 +#define CIRRUS_SR7_BPP_32 0x08 +#define CIRRUS_SR7_ISAADDR_MASK 0xe0 + +// sequencer 0x0f +#define CIRRUS_MEMSIZE_512k 0x08 +#define CIRRUS_MEMSIZE_1M 0x10 +#define CIRRUS_MEMSIZE_2M 0x18 +#define CIRRUS_MEMFLAGS_BANKSWITCH 0x80 // bank switching is enabled. + +// sequencer 0x12 +#define CIRRUS_CURSOR_SHOW 0x01 +#define CIRRUS_CURSOR_HIDDENPEL 0x02 +#define CIRRUS_CURSOR_LARGE 0x04 // 64x64 if set, 32x32 if clear + +// sequencer 0x17 +#define CIRRUS_BUSTYPE_VLBFAST 0x10 +#define CIRRUS_BUSTYPE_PCI 0x20 +#define CIRRUS_BUSTYPE_VLBSLOW 0x30 +#define CIRRUS_BUSTYPE_ISA 0x38 +#define CIRRUS_MMIO_ENABLE 0x04 +#define CIRRUS_MMIO_USE_PCIADDR 0x40 // 0xb8000 if cleared. +#define CIRRUS_MEMSIZEEXT_DOUBLE 0x80 + +// control 0x0b +#define CIRRUS_BANKING_DUAL 0x01 +#define CIRRUS_BANKING_GRANULARITY_16K 0x20 // set:16k, clear:4k + +// control 0x30 +#define CIRRUS_BLTMODE_BACKWARDS 0x01 +#define CIRRUS_BLTMODE_MEMSYSDEST 0x02 +#define CIRRUS_BLTMODE_MEMSYSSRC 0x04 +#define CIRRUS_BLTMODE_TRANSPARENTCOMP 0x08 +#define CIRRUS_BLTMODE_PATTERNCOPY 0x40 +#define CIRRUS_BLTMODE_COLOREXPAND 0x80 +#define CIRRUS_BLTMODE_PIXELWIDTHMASK 0x30 +#define CIRRUS_BLTMODE_PIXELWIDTH8 0x00 +#define CIRRUS_BLTMODE_PIXELWIDTH16 0x10 +#define CIRRUS_BLTMODE_PIXELWIDTH24 0x20 +#define CIRRUS_BLTMODE_PIXELWIDTH32 0x30 + +// control 0x31 +#define CIRRUS_BLT_BUSY 0x01 +#define CIRRUS_BLT_START 0x02 +#define CIRRUS_BLT_RESET 0x04 +#define CIRRUS_BLT_FIFOUSED 0x10 +#define CIRRUS_BLT_AUTOSTART 0x80 + +// control 0x32 +#define CIRRUS_ROP_0 0x00 +#define CIRRUS_ROP_SRC_AND_DST 0x05 +#define CIRRUS_ROP_NOP 0x06 +#define CIRRUS_ROP_SRC_AND_NOTDST 0x09 +#define CIRRUS_ROP_NOTDST 0x0b +#define CIRRUS_ROP_SRC 0x0d +#define CIRRUS_ROP_1 0x0e +#define CIRRUS_ROP_NOTSRC_AND_DST 0x50 +#define CIRRUS_ROP_SRC_XOR_DST 0x59 +#define CIRRUS_ROP_SRC_OR_DST 0x6d +#define CIRRUS_ROP_NOTSRC_OR_NOTDST 0x90 +#define CIRRUS_ROP_SRC_NOTXOR_DST 0x95 +#define CIRRUS_ROP_SRC_OR_NOTDST 0xad +#define CIRRUS_ROP_NOTSRC 0xd0 +#define CIRRUS_ROP_NOTSRC_OR_DST 0xd6 +#define CIRRUS_ROP_NOTSRC_AND_NOTDST 0xda + +#define CIRRUS_ROP_NOP_INDEX 2 +#define CIRRUS_ROP_SRC_INDEX 5 + +// control 0x33 +#define CIRRUS_BLTMODEEXT_SOLIDFILL 0x04 +#define CIRRUS_BLTMODEEXT_COLOREXPINV 0x02 +#define CIRRUS_BLTMODEEXT_DWORDGRANULARITY 0x01 + +// memory-mapped IO +#define CIRRUS_MMIO_BLTBGCOLOR 0x00 // dword +#define CIRRUS_MMIO_BLTFGCOLOR 0x04 // dword +#define CIRRUS_MMIO_BLTWIDTH 0x08 // word +#define CIRRUS_MMIO_BLTHEIGHT 0x0a // word +#define CIRRUS_MMIO_BLTDESTPITCH 0x0c // word +#define CIRRUS_MMIO_BLTSRCPITCH 0x0e // word +#define CIRRUS_MMIO_BLTDESTADDR 0x10 // dword +#define CIRRUS_MMIO_BLTSRCADDR 0x14 // dword +#define CIRRUS_MMIO_BLTWRITEMASK 0x17 // byte +#define CIRRUS_MMIO_BLTMODE 0x18 // byte +#define CIRRUS_MMIO_BLTROP 0x1a // byte +#define CIRRUS_MMIO_BLTMODEEXT 0x1b // byte +#define CIRRUS_MMIO_BLTTRANSPARENTCOLOR 0x1c // word? +#define CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK 0x20 // word? +#define CIRRUS_MMIO_LINEARDRAW_START_X 0x24 // word +#define CIRRUS_MMIO_LINEARDRAW_START_Y 0x26 // word +#define CIRRUS_MMIO_LINEARDRAW_END_X 0x28 // word +#define CIRRUS_MMIO_LINEARDRAW_END_Y 0x2a // word +#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_INC 0x2c // byte +#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ROLLOVER 0x2d // byte +#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_MASK 0x2e // byte +#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ACCUM 0x2f // byte +#define CIRRUS_MMIO_BRESENHAM_K1 0x30 // word +#define CIRRUS_MMIO_BRESENHAM_K3 0x32 // word +#define CIRRUS_MMIO_BRESENHAM_ERROR 0x34 // word +#define CIRRUS_MMIO_BRESENHAM_DELTA_MAJOR 0x36 // word +#define CIRRUS_MMIO_BRESENHAM_DIRECTION 0x38 // byte +#define CIRRUS_MMIO_LINEDRAW_MODE 0x39 // byte +#define CIRRUS_MMIO_BLTSTATUS 0x40 // byte + +// PCI 0x00: vendor, 0x02: device +#define PCI_VENDOR_CIRRUS 0x1013 +#define PCI_DEVICE_CLGD5462 0x00d0 +#define PCI_DEVICE_CLGD5465 0x00d6 + +// PCI 0x04: command(word), 0x06(word): status +#define PCI_COMMAND_IOACCESS 0x0001 +#define PCI_COMMAND_MEMACCESS 0x0002 +#define PCI_COMMAND_BUSMASTER 0x0004 +#define PCI_COMMAND_SPECIALCYCLE 0x0008 +#define PCI_COMMAND_MEMWRITEINVALID 0x0010 +#define PCI_COMMAND_PALETTESNOOPING 0x0020 +#define PCI_COMMAND_PARITYDETECTION 0x0040 +#define PCI_COMMAND_ADDRESSDATASTEPPING 0x0080 +#define PCI_COMMAND_SERR 0x0100 +#define PCI_COMMAND_BACKTOBACKTRANS 0x0200 +// PCI 0x08, 0xff000000 (0x09-0x0b:class,0x08:rev) +#define PCI_CLASS_BASE_DISPLAY 0x03 +// PCI 0x08, 0x00ff0000 +#define PCI_CLASS_SUB_VGA 0x00 +// PCI 0x0c, 0x00ff0000 (0x0c:cacheline,0x0d:latency,0x0e:headertype,0x0f:Built-in self test) +#define PCI_CLASS_HEADERTYPE_00h 0x00 +// 0x10-0x3f (headertype 00h) +// PCI 0x10,0x14,0x18,0x1c,0x20,0x24: base address mapping registers +// 0x10: MEMBASE, 0x14: IOBASE(hard-coded in XFree86 3.x) +#define PCI_MAP_MEM 0x0 +#define PCI_MAP_IO 0x1 +#define PCI_MAP_MEM_ADDR_MASK (~0xf) +#define PCI_MAP_IO_ADDR_MASK (~0x3) +#define PCI_MAP_MEMFLAGS_32BIT 0x0 +#define PCI_MAP_MEMFLAGS_32BIT_1M 0x1 +#define PCI_MAP_MEMFLAGS_64BIT 0x4 +#define PCI_MAP_MEMFLAGS_CACHEABLE 0x8 +// PCI 0x28: cardbus CIS pointer +// PCI 0x2c: subsystem vendor id, 0x2e: subsystem id +// PCI 0x30: expansion ROM base address +#define PCI_ROMBIOS_ENABLED 0x1 +// PCI 0x34: 0xffffff00=reserved, 0x000000ff=capabilities pointer +// PCI 0x38: reserved +// PCI 0x3c: 0x3c=int-line, 0x3d=int-pin, 0x3e=min-gnt, 0x3f=maax-lat + +#define CIRRUS_PNPMMIO_SIZE 0x1000 + + +/* I/O and memory hook */ +#define CIRRUS_HOOK_NOT_HANDLED 0 +#define CIRRUS_HOOK_HANDLED 1 + +struct CirrusVGAState; +typedef void (*cirrus_bitblt_rop_t) (struct CirrusVGAState *s, + uint8_t * dst, const uint8_t * src, + int dstpitch, int srcpitch, + int bltwidth, int bltheight); +typedef void (*cirrus_fill_t)(struct CirrusVGAState *s, + uint8_t *dst, int dst_pitch, int width, int height); + +typedef struct CirrusVGAState { + VGA_STATE_COMMON + + int cirrus_linear_io_addr; + int cirrus_linear_bitblt_io_addr; + int cirrus_mmio_io_addr; + unsigned long cirrus_lfb_addr; + unsigned long cirrus_lfb_end; + uint32_t cirrus_addr_mask; + uint32_t linear_mmio_mask; + uint8_t cirrus_shadow_gr0; + uint8_t cirrus_shadow_gr1; + uint8_t cirrus_hidden_dac_lockindex; + uint8_t cirrus_hidden_dac_data; + uint32_t cirrus_bank_base[2]; + uint32_t cirrus_bank_limit[2]; + uint8_t cirrus_hidden_palette[48]; + uint32_t hw_cursor_x; + uint32_t hw_cursor_y; + int cirrus_blt_pixelwidth; + int cirrus_blt_width; + int cirrus_blt_height; + int cirrus_blt_dstpitch; + int cirrus_blt_srcpitch; + uint32_t cirrus_blt_fgcol; + uint32_t cirrus_blt_bgcol; + uint32_t cirrus_blt_dstaddr; + uint32_t cirrus_blt_srcaddr; + uint8_t cirrus_blt_mode; + uint8_t cirrus_blt_modeext; + cirrus_bitblt_rop_t cirrus_rop; +#define CIRRUS_BLTBUFSIZE (2048 * 4) /* one line width */ + uint8_t cirrus_bltbuf[CIRRUS_BLTBUFSIZE]; + uint8_t *cirrus_srcptr; + uint8_t *cirrus_srcptr_end; + uint32_t cirrus_srccounter; + /* hwcursor display state */ + int last_hw_cursor_size; + int last_hw_cursor_x; + int last_hw_cursor_y; + int last_hw_cursor_y_start; + int last_hw_cursor_y_end; + int real_vram_size; /* XXX: suppress that */ + CPUWriteMemoryFunc **cirrus_linear_write; + unsigned long map_addr; + unsigned long map_end; +} CirrusVGAState; + +typedef struct PCICirrusVGAState { + PCIDevice dev; + CirrusVGAState cirrus_vga; +} PCICirrusVGAState; + +static uint8_t rop_to_index[256]; + +void *shared_vram; + +/*************************************** + * + * prototypes. + * + ***************************************/ + + +static void cirrus_bitblt_reset(CirrusVGAState *s); +static void cirrus_update_memory_access(CirrusVGAState *s); + +/*************************************** + * + * raster operations + * + ***************************************/ + +static void cirrus_bitblt_rop_nop(CirrusVGAState *s, + uint8_t *dst,const uint8_t *src, + int dstpitch,int srcpitch, + int bltwidth,int bltheight) +{ +} + +static void cirrus_bitblt_fill_nop(CirrusVGAState *s, + uint8_t *dst, + int dstpitch, int bltwidth,int bltheight) +{ +} + +#define ROP_NAME 0 +#define ROP_OP(d, s) d = 0 +#include "cirrus_vga_rop.h" + +#define ROP_NAME src_and_dst +#define ROP_OP(d, s) d = (s) & (d) +#include "cirrus_vga_rop.h" + +#define ROP_NAME src_and_notdst +#define ROP_OP(d, s) d = (s) & (~(d)) +#include "cirrus_vga_rop.h" + +#define ROP_NAME notdst +#define ROP_OP(d, s) d = ~(d) +#include "cirrus_vga_rop.h" + +#define ROP_NAME src +#define ROP_OP(d, s) d = s +#include "cirrus_vga_rop.h" + +#define ROP_NAME 1 +#define ROP_OP(d, s) d = ~0 +#include "cirrus_vga_rop.h" + +#define ROP_NAME notsrc_and_dst +#define ROP_OP(d, s) d = (~(s)) & (d) +#include "cirrus_vga_rop.h" + +#define ROP_NAME src_xor_dst +#define ROP_OP(d, s) d = (s) ^ (d) +#include "cirrus_vga_rop.h" + +#define ROP_NAME src_or_dst +#define ROP_OP(d, s) d = (s) | (d) +#include "cirrus_vga_rop.h" + +#define ROP_NAME notsrc_or_notdst +#define ROP_OP(d, s) d = (~(s)) | (~(d)) +#include "cirrus_vga_rop.h" + +#define ROP_NAME src_notxor_dst +#define ROP_OP(d, s) d = ~((s) ^ (d)) +#include "cirrus_vga_rop.h" + +#define ROP_NAME src_or_notdst +#define ROP_OP(d, s) d = (s) | (~(d)) +#include "cirrus_vga_rop.h" + +#define ROP_NAME notsrc +#define ROP_OP(d, s) d = (~(s)) +#include "cirrus_vga_rop.h" + +#define ROP_NAME notsrc_or_dst +#define ROP_OP(d, s) d = (~(s)) | (d) +#include "cirrus_vga_rop.h" + +#define ROP_NAME notsrc_and_notdst +#define ROP_OP(d, s) d = (~(s)) & (~(d)) +#include "cirrus_vga_rop.h" + +static const cirrus_bitblt_rop_t cirrus_fwd_rop[16] = { + cirrus_bitblt_rop_fwd_0, + cirrus_bitblt_rop_fwd_src_and_dst, + cirrus_bitblt_rop_nop, + cirrus_bitblt_rop_fwd_src_and_notdst, + cirrus_bitblt_rop_fwd_notdst, + cirrus_bitblt_rop_fwd_src, + cirrus_bitblt_rop_fwd_1, + cirrus_bitblt_rop_fwd_notsrc_and_dst, + cirrus_bitblt_rop_fwd_src_xor_dst, + cirrus_bitblt_rop_fwd_src_or_dst, + cirrus_bitblt_rop_fwd_notsrc_or_notdst, + cirrus_bitblt_rop_fwd_src_notxor_dst, + cirrus_bitblt_rop_fwd_src_or_notdst, + cirrus_bitblt_rop_fwd_notsrc, + cirrus_bitblt_rop_fwd_notsrc_or_dst, + cirrus_bitblt_rop_fwd_notsrc_and_notdst, +}; + +static const cirrus_bitblt_rop_t cirrus_bkwd_rop[16] = { + cirrus_bitblt_rop_bkwd_0, + cirrus_bitblt_rop_bkwd_src_and_dst, + cirrus_bitblt_rop_nop, + cirrus_bitblt_rop_bkwd_src_and_notdst, + cirrus_bitblt_rop_bkwd_notdst, + cirrus_bitblt_rop_bkwd_src, + cirrus_bitblt_rop_bkwd_1, + cirrus_bitblt_rop_bkwd_notsrc_and_dst, + cirrus_bitblt_rop_bkwd_src_xor_dst, + cirrus_bitblt_rop_bkwd_src_or_dst, + cirrus_bitblt_rop_bkwd_notsrc_or_notdst, + cirrus_bitblt_rop_bkwd_src_notxor_dst, + cirrus_bitblt_rop_bkwd_src_or_notdst, + cirrus_bitblt_rop_bkwd_notsrc, + cirrus_bitblt_rop_bkwd_notsrc_or_dst, + cirrus_bitblt_rop_bkwd_notsrc_and_notdst, +}; + +#define ROP2(name) {\ + name ## _8,\ + name ## _16,\ + name ## _24,\ + name ## _32,\ + } + +#define ROP_NOP2(func) {\ + func,\ + func,\ + func,\ + func,\ + } + +static const cirrus_bitblt_rop_t cirrus_patternfill[16][4] = { + ROP2(cirrus_patternfill_0), + ROP2(cirrus_patternfill_src_and_dst), + ROP_NOP2(cirrus_bitblt_rop_nop), + ROP2(cirrus_patternfill_src_and_notdst), + ROP2(cirrus_patternfill_notdst), + ROP2(cirrus_patternfill_src), + ROP2(cirrus_patternfill_1), + ROP2(cirrus_patternfill_notsrc_and_dst), + ROP2(cirrus_patternfill_src_xor_dst), + ROP2(cirrus_patternfill_src_or_dst), + ROP2(cirrus_patternfill_notsrc_or_notdst), + ROP2(cirrus_patternfill_src_notxor_dst), + ROP2(cirrus_patternfill_src_or_notdst), + ROP2(cirrus_patternfill_notsrc), + ROP2(cirrus_patternfill_notsrc_or_dst), + ROP2(cirrus_patternfill_notsrc_and_notdst), +}; + +static const cirrus_bitblt_rop_t cirrus_colorexpand_transp[16][4] = { + ROP2(cirrus_colorexpand_transp_0), + ROP2(cirrus_colorexpand_transp_src_and_dst), + ROP_NOP2(cirrus_bitblt_rop_nop), + ROP2(cirrus_colorexpand_transp_src_and_notdst), + ROP2(cirrus_colorexpand_transp_notdst), + ROP2(cirrus_colorexpand_transp_src), + ROP2(cirrus_colorexpand_transp_1), + ROP2(cirrus_colorexpand_transp_notsrc_and_dst), + ROP2(cirrus_colorexpand_transp_src_xor_dst), + ROP2(cirrus_colorexpand_transp_src_or_dst), + ROP2(cirrus_colorexpand_transp_notsrc_or_notdst), + ROP2(cirrus_colorexpand_transp_src_notxor_dst), + ROP2(cirrus_colorexpand_transp_src_or_notdst), + ROP2(cirrus_colorexpand_transp_notsrc), + ROP2(cirrus_colorexpand_transp_notsrc_or_dst), + ROP2(cirrus_colorexpand_transp_notsrc_and_notdst), +}; + +static const cirrus_bitblt_rop_t cirrus_colorexpand[16][4] = { + ROP2(cirrus_colorexpand_0), + ROP2(cirrus_colorexpand_src_and_dst), + ROP_NOP2(cirrus_bitblt_rop_nop), + ROP2(cirrus_colorexpand_src_and_notdst), + ROP2(cirrus_colorexpand_notdst), + ROP2(cirrus_colorexpand_src), + ROP2(cirrus_colorexpand_1), + ROP2(cirrus_colorexpand_notsrc_and_dst), + ROP2(cirrus_colorexpand_src_xor_dst), + ROP2(cirrus_colorexpand_src_or_dst), + ROP2(cirrus_colorexpand_notsrc_or_notdst), + ROP2(cirrus_colorexpand_src_notxor_dst), + ROP2(cirrus_colorexpand_src_or_notdst), + ROP2(cirrus_colorexpand_notsrc), + ROP2(cirrus_colorexpand_notsrc_or_dst), + ROP2(cirrus_colorexpand_notsrc_and_notdst), +}; + +static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern_transp[16][4] = { + ROP2(cirrus_colorexpand_pattern_transp_0), + ROP2(cirrus_colorexpand_pattern_transp_src_and_dst), + ROP_NOP2(cirrus_bitblt_rop_nop), + ROP2(cirrus_colorexpand_pattern_transp_src_and_notdst), + ROP2(cirrus_colorexpand_pattern_transp_notdst), + ROP2(cirrus_colorexpand_pattern_transp_src), + ROP2(cirrus_colorexpand_pattern_transp_1), + ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_dst), + ROP2(cirrus_colorexpand_pattern_transp_src_xor_dst), + ROP2(cirrus_colorexpand_pattern_transp_src_or_dst), + ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_notdst), + ROP2(cirrus_colorexpand_pattern_transp_src_notxor_dst), + ROP2(cirrus_colorexpand_pattern_transp_src_or_notdst), + ROP2(cirrus_colorexpand_pattern_transp_notsrc), + ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_dst), + ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_notdst), +}; + +static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern[16][4] = { + ROP2(cirrus_colorexpand_pattern_0), + ROP2(cirrus_colorexpand_pattern_src_and_dst), + ROP_NOP2(cirrus_bitblt_rop_nop), + ROP2(cirrus_colorexpand_pattern_src_and_notdst), + ROP2(cirrus_colorexpand_pattern_notdst), + ROP2(cirrus_colorexpand_pattern_src), + ROP2(cirrus_colorexpand_pattern_1), + ROP2(cirrus_colorexpand_pattern_notsrc_and_dst), + ROP2(cirrus_colorexpand_pattern_src_xor_dst), + ROP2(cirrus_colorexpand_pattern_src_or_dst), + ROP2(cirrus_colorexpand_pattern_notsrc_or_notdst), + ROP2(cirrus_colorexpand_pattern_src_notxor_dst), + ROP2(cirrus_colorexpand_pattern_src_or_notdst), + ROP2(cirrus_colorexpand_pattern_notsrc), + ROP2(cirrus_colorexpand_pattern_notsrc_or_dst), + ROP2(cirrus_colorexpand_pattern_notsrc_and_notdst), +}; + +static const cirrus_fill_t cirrus_fill[16][4] = { + ROP2(cirrus_fill_0), + ROP2(cirrus_fill_src_and_dst), + ROP_NOP2(cirrus_bitblt_fill_nop), + ROP2(cirrus_fill_src_and_notdst), + ROP2(cirrus_fill_notdst), + ROP2(cirrus_fill_src), + ROP2(cirrus_fill_1), + ROP2(cirrus_fill_notsrc_and_dst), + ROP2(cirrus_fill_src_xor_dst), + ROP2(cirrus_fill_src_or_dst), + ROP2(cirrus_fill_notsrc_or_notdst), + ROP2(cirrus_fill_src_notxor_dst), + ROP2(cirrus_fill_src_or_notdst), + ROP2(cirrus_fill_notsrc), + ROP2(cirrus_fill_notsrc_or_dst), + ROP2(cirrus_fill_notsrc_and_notdst), +}; + +static inline void cirrus_bitblt_fgcol(CirrusVGAState *s) +{ + unsigned int color; + switch (s->cirrus_blt_pixelwidth) { + case 1: + s->cirrus_blt_fgcol = s->cirrus_shadow_gr1; + break; + case 2: + color = s->cirrus_shadow_gr1 | (s->gr[0x11] << 8); + s->cirrus_blt_fgcol = le16_to_cpu(color); + break; + case 3: + s->cirrus_blt_fgcol = s->cirrus_shadow_gr1 | + (s->gr[0x11] << 8) | (s->gr[0x13] << 16); + break; + default: + case 4: + color = s->cirrus_shadow_gr1 | (s->gr[0x11] << 8) | + (s->gr[0x13] << 16) | (s->gr[0x15] << 24); + s->cirrus_blt_fgcol = le32_to_cpu(color); + break; + } +} + +static inline void cirrus_bitblt_bgcol(CirrusVGAState *s) +{ + unsigned int color; + switch (s->cirrus_blt_pixelwidth) { + case 1: + s->cirrus_blt_bgcol = s->cirrus_shadow_gr0; + break; + case 2: + color = s->cirrus_shadow_gr0 | (s->gr[0x10] << 8); + s->cirrus_blt_bgcol = le16_to_cpu(color); + break; + case 3: + s->cirrus_blt_bgcol = s->cirrus_shadow_gr0 | + (s->gr[0x10] << 8) | (s->gr[0x12] << 16); + break; + default: + case 4: + color = s->cirrus_shadow_gr0 | (s->gr[0x10] << 8) | + (s->gr[0x12] << 16) | (s->gr[0x14] << 24); + s->cirrus_blt_bgcol = le32_to_cpu(color); + break; + } +} + +static void cirrus_invalidate_region(CirrusVGAState * s, int off_begin, + int off_pitch, int bytesperline, + int lines) +{ + int y; + int off_cur; + int off_cur_end; + + for (y = 0; y < lines; y++) { + off_cur = off_begin; + off_cur_end = off_cur + bytesperline; + off_cur &= TARGET_PAGE_MASK; + while (off_cur < off_cur_end) { + cpu_physical_memory_set_dirty(s->vram_offset + off_cur); + off_cur += TARGET_PAGE_SIZE; + } + off_begin += off_pitch; + } +} + +static int cirrus_bitblt_common_patterncopy(CirrusVGAState * s, + const uint8_t * src) +{ + uint8_t *dst; + + dst = s->vram_ptr + s->cirrus_blt_dstaddr; + (*s->cirrus_rop) (s, dst, src, + s->cirrus_blt_dstpitch, 0, + s->cirrus_blt_width, s->cirrus_blt_height); + cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, + s->cirrus_blt_dstpitch, s->cirrus_blt_width, + s->cirrus_blt_height); + return 1; +} + +/* fill */ + +static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop) +{ + cirrus_fill_t rop_func; + + rop_func = cirrus_fill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + rop_func(s, s->vram_ptr + s->cirrus_blt_dstaddr, + s->cirrus_blt_dstpitch, + s->cirrus_blt_width, s->cirrus_blt_height); + cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, + s->cirrus_blt_dstpitch, s->cirrus_blt_width, + s->cirrus_blt_height); + cirrus_bitblt_reset(s); + return 1; +} + +/*************************************** + * + * bitblt (video-to-video) + * + ***************************************/ + +static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s) +{ + return cirrus_bitblt_common_patterncopy(s, + s->vram_ptr + + (s->cirrus_blt_srcaddr & ~7)); +} + +static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) +{ + int sx, sy; + int dx, dy; + int width, height; + int depth; + int notify = 0; + + depth = s->get_bpp((VGAState *)s) / 8; + s->get_resolution((VGAState *)s, &width, &height); + + /* extra x, y */ + sx = (src % (width * depth)) / depth; + sy = (src / (width * depth)); + dx = (dst % (width *depth)) / depth; + dy = (dst / (width * depth)); + + /* normalize width */ + w /= depth; + + /* if we're doing a backward copy, we have to adjust + our x/y to be the upper left corner (instead of the lower + right corner) */ + if (s->cirrus_blt_dstpitch < 0) { + sx -= (s->cirrus_blt_width / depth) - 1; + dx -= (s->cirrus_blt_width / depth) - 1; + sy -= s->cirrus_blt_height - 1; + dy -= s->cirrus_blt_height - 1; + } + + /* are we in the visible portion of memory? */ + if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 && + (sx + w) <= width && (sy + h) <= height && + (dx + w) <= width && (dy + h) <= height) { + notify = 1; + } + + /* make to sure only copy if it's a plain copy ROP */ + if (*s->cirrus_rop != cirrus_bitblt_rop_fwd_src && + *s->cirrus_rop != cirrus_bitblt_rop_bkwd_src) + notify = 0; + + /* we have to flush all pending changes so that the copy + is generated at the appropriate moment in time */ + if (notify) + vga_hw_update(); + + (*s->cirrus_rop) (s, s->vram_ptr + s->cirrus_blt_dstaddr, + s->vram_ptr + s->cirrus_blt_srcaddr, + s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch, + s->cirrus_blt_width, s->cirrus_blt_height); + + if (notify) + s->ds->dpy_copy(s->ds, + sx, sy, dx, dy, + s->cirrus_blt_width / depth, + s->cirrus_blt_height); + + /* we don't have to notify the display that this portion has + changed since dpy_copy implies this */ + + if (!notify) + cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, + s->cirrus_blt_dstpitch, s->cirrus_blt_width, + s->cirrus_blt_height); +} + +static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s) +{ + if (s->ds->dpy_copy) { + cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->start_addr, + s->cirrus_blt_srcaddr - s->start_addr, + s->cirrus_blt_width, s->cirrus_blt_height); + } else { + (*s->cirrus_rop) (s, s->vram_ptr + s->cirrus_blt_dstaddr, + s->vram_ptr + s->cirrus_blt_srcaddr, + s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch, + s->cirrus_blt_width, s->cirrus_blt_height); + + cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, + s->cirrus_blt_dstpitch, s->cirrus_blt_width, + s->cirrus_blt_height); + } + + return 1; +} + +/*************************************** + * + * bitblt (cpu-to-video) + * + ***************************************/ + +static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s) +{ + int copy_count; + uint8_t *end_ptr; + + if (s->cirrus_srccounter > 0) { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { + cirrus_bitblt_common_patterncopy(s, s->cirrus_bltbuf); + the_end: + s->cirrus_srccounter = 0; + cirrus_bitblt_reset(s); + } else { + /* at least one scan line */ + do { + (*s->cirrus_rop)(s, s->vram_ptr + s->cirrus_blt_dstaddr, + s->cirrus_bltbuf, 0, 0, s->cirrus_blt_width, 1); + cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, 0, + s->cirrus_blt_width, 1); + s->cirrus_blt_dstaddr += s->cirrus_blt_dstpitch; + s->cirrus_srccounter -= s->cirrus_blt_srcpitch; + if (s->cirrus_srccounter <= 0) + goto the_end; + /* more bytes than needed can be transfered because of + word alignment, so we keep them for the next line */ + /* XXX: keep alignment to speed up transfer */ + end_ptr = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; + copy_count = s->cirrus_srcptr_end - end_ptr; + memmove(s->cirrus_bltbuf, end_ptr, copy_count); + s->cirrus_srcptr = s->cirrus_bltbuf + copy_count; + s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; + } while (s->cirrus_srcptr >= s->cirrus_srcptr_end); + } + } +} + +/*************************************** + * + * bitblt wrapper + * + ***************************************/ + +static void cirrus_bitblt_reset(CirrusVGAState * s) +{ + s->gr[0x31] &= + ~(CIRRUS_BLT_START | CIRRUS_BLT_BUSY | CIRRUS_BLT_FIFOUSED); + s->cirrus_srcptr = &s->cirrus_bltbuf[0]; + s->cirrus_srcptr_end = &s->cirrus_bltbuf[0]; + s->cirrus_srccounter = 0; + cirrus_update_memory_access(s); +} + +static int cirrus_bitblt_cputovideo(CirrusVGAState * s) +{ + int w; + + s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_MEMSYSSRC; + s->cirrus_srcptr = &s->cirrus_bltbuf[0]; + s->cirrus_srcptr_end = &s->cirrus_bltbuf[0]; + + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { + s->cirrus_blt_srcpitch = 8; + } else { + /* XXX: check for 24 bpp */ + s->cirrus_blt_srcpitch = 8 * 8 * s->cirrus_blt_pixelwidth; + } + s->cirrus_srccounter = s->cirrus_blt_srcpitch; + } else { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { + w = s->cirrus_blt_width / s->cirrus_blt_pixelwidth; + if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_DWORDGRANULARITY) + s->cirrus_blt_srcpitch = ((w + 31) >> 5); + else + s->cirrus_blt_srcpitch = ((w + 7) >> 3); + } else { + /* always align input size to 32 bits */ + s->cirrus_blt_srcpitch = (s->cirrus_blt_width + 3) & ~3; + } + s->cirrus_srccounter = s->cirrus_blt_srcpitch * s->cirrus_blt_height; + } + s->cirrus_srcptr = s->cirrus_bltbuf; + s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; + cirrus_update_memory_access(s); + return 1; +} + +static int cirrus_bitblt_videotocpu(CirrusVGAState * s) +{ + /* XXX */ +#ifdef DEBUG_BITBLT + printf("cirrus: bitblt (video to cpu) is not implemented yet\n"); +#endif + return 0; +} + +static int cirrus_bitblt_videotovideo(CirrusVGAState * s) +{ + int ret; + + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { + ret = cirrus_bitblt_videotovideo_patterncopy(s); + } else { + ret = cirrus_bitblt_videotovideo_copy(s); + } + if (ret) + cirrus_bitblt_reset(s); + return ret; +} + +static void cirrus_bitblt_start(CirrusVGAState * s) +{ + uint8_t blt_rop; + + s->gr[0x31] |= CIRRUS_BLT_BUSY; + + s->cirrus_blt_width = (s->gr[0x20] | (s->gr[0x21] << 8)) + 1; + s->cirrus_blt_height = (s->gr[0x22] | (s->gr[0x23] << 8)) + 1; + s->cirrus_blt_dstpitch = (s->gr[0x24] | (s->gr[0x25] << 8)); + s->cirrus_blt_srcpitch = (s->gr[0x26] | (s->gr[0x27] << 8)); + s->cirrus_blt_dstaddr = + (s->gr[0x28] | (s->gr[0x29] << 8) | (s->gr[0x2a] << 16)); + s->cirrus_blt_srcaddr = + (s->gr[0x2c] | (s->gr[0x2d] << 8) | (s->gr[0x2e] << 16)); + s->cirrus_blt_mode = s->gr[0x30]; + s->cirrus_blt_modeext = s->gr[0x33]; + blt_rop = s->gr[0x32]; + +#ifdef DEBUG_BITBLT + printf("rop=0x%02x mode=0x%02x modeext=0x%02x w=%d h=%d dpitch=%d spitch=%d daddr=0x%08x saddr=0x%08x writemask=0x%02x\n", + blt_rop, + s->cirrus_blt_mode, + s->cirrus_blt_modeext, + s->cirrus_blt_width, + s->cirrus_blt_height, + s->cirrus_blt_dstpitch, + s->cirrus_blt_srcpitch, + s->cirrus_blt_dstaddr, + s->cirrus_blt_srcaddr, + s->gr[0x2f]); +#endif + + switch (s->cirrus_blt_mode & CIRRUS_BLTMODE_PIXELWIDTHMASK) { + case CIRRUS_BLTMODE_PIXELWIDTH8: + s->cirrus_blt_pixelwidth = 1; + break; + case CIRRUS_BLTMODE_PIXELWIDTH16: + s->cirrus_blt_pixelwidth = 2; + break; + case CIRRUS_BLTMODE_PIXELWIDTH24: + s->cirrus_blt_pixelwidth = 3; + break; + case CIRRUS_BLTMODE_PIXELWIDTH32: + s->cirrus_blt_pixelwidth = 4; + break; + default: +#ifdef DEBUG_BITBLT + printf("cirrus: bitblt - pixel width is unknown\n"); +#endif + goto bitblt_ignore; + } + s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_PIXELWIDTHMASK; + + if ((s-> + cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSSRC | + CIRRUS_BLTMODE_MEMSYSDEST)) + == (CIRRUS_BLTMODE_MEMSYSSRC | CIRRUS_BLTMODE_MEMSYSDEST)) { +#ifdef DEBUG_BITBLT + printf("cirrus: bitblt - memory-to-memory copy is requested\n"); +#endif + goto bitblt_ignore; + } + + if ((s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_SOLIDFILL) && + (s->cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSDEST | + CIRRUS_BLTMODE_TRANSPARENTCOMP | + CIRRUS_BLTMODE_PATTERNCOPY | + CIRRUS_BLTMODE_COLOREXPAND)) == + (CIRRUS_BLTMODE_PATTERNCOPY | CIRRUS_BLTMODE_COLOREXPAND)) { + cirrus_bitblt_fgcol(s); + cirrus_bitblt_solidfill(s, blt_rop); + } else { + if ((s->cirrus_blt_mode & (CIRRUS_BLTMODE_COLOREXPAND | + CIRRUS_BLTMODE_PATTERNCOPY)) == + CIRRUS_BLTMODE_COLOREXPAND) { + + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) { + if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) + cirrus_bitblt_bgcol(s); + else + cirrus_bitblt_fgcol(s); + s->cirrus_rop = cirrus_colorexpand_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } else { + cirrus_bitblt_fgcol(s); + cirrus_bitblt_bgcol(s); + s->cirrus_rop = cirrus_colorexpand[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } + } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) { + if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) + cirrus_bitblt_bgcol(s); + else + cirrus_bitblt_fgcol(s); + s->cirrus_rop = cirrus_colorexpand_pattern_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } else { + cirrus_bitblt_fgcol(s); + cirrus_bitblt_bgcol(s); + s->cirrus_rop = cirrus_colorexpand_pattern[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } + } else { + s->cirrus_rop = cirrus_patternfill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } + } else { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) { + s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch; + s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch; + s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]]; + } else { + s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]]; + } + } + + // setup bitblt engine. + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSSRC) { + if (!cirrus_bitblt_cputovideo(s)) + goto bitblt_ignore; + } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSDEST) { + if (!cirrus_bitblt_videotocpu(s)) + goto bitblt_ignore; + } else { + if (!cirrus_bitblt_videotovideo(s)) + goto bitblt_ignore; + } + } + return; + bitblt_ignore:; + cirrus_bitblt_reset(s); +} + +static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value) +{ + unsigned old_value; + + old_value = s->gr[0x31]; + s->gr[0x31] = reg_value; + + if (((old_value & CIRRUS_BLT_RESET) != 0) && + ((reg_value & CIRRUS_BLT_RESET) == 0)) { + cirrus_bitblt_reset(s); + } else if (((old_value & CIRRUS_BLT_START) == 0) && + ((reg_value & CIRRUS_BLT_START) != 0)) { + cirrus_bitblt_start(s); + } +} + + +/*************************************** + * + * basic parameters + * + ***************************************/ + +static void cirrus_get_offsets(VGAState *s1, + uint32_t *pline_offset, + uint32_t *pstart_addr) +{ + CirrusVGAState * s = (CirrusVGAState *)s1; + uint32_t start_addr; + uint32_t line_offset; + + line_offset = s->cr[0x13] + | ((s->cr[0x1b] & 0x10) << 4); + line_offset <<= 3; + *pline_offset = line_offset; + + start_addr = (s->cr[0x0c] << 8) + | s->cr[0x0d] + | ((s->cr[0x1b] & 0x01) << 16) + | ((s->cr[0x1b] & 0x0c) << 15) + | ((s->cr[0x1d] & 0x80) << 12); + *pstart_addr = start_addr; +} + +static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s) +{ + uint32_t ret = 16; + + switch (s->cirrus_hidden_dac_data & 0xf) { + case 0: + ret = 15; + break; /* Sierra HiColor */ + case 1: + ret = 16; + break; /* XGA HiColor */ + default: +#ifdef DEBUG_CIRRUS + printf("cirrus: invalid DAC value %x in 16bpp\n", + (s->cirrus_hidden_dac_data & 0xf)); +#endif + ret = 15; /* XXX */ + break; + } + return ret; +} + +static int cirrus_get_bpp(VGAState *s1) +{ + CirrusVGAState * s = (CirrusVGAState *)s1; + uint32_t ret = 8; + + if ((s->sr[0x07] & 0x01) != 0) { + /* Cirrus SVGA */ + switch (s->sr[0x07] & CIRRUS_SR7_BPP_MASK) { + case CIRRUS_SR7_BPP_8: + ret = 8; + break; + case CIRRUS_SR7_BPP_16_DOUBLEVCLK: + ret = cirrus_get_bpp16_depth(s); + break; + case CIRRUS_SR7_BPP_24: + ret = 24; + break; + case CIRRUS_SR7_BPP_16: + ret = cirrus_get_bpp16_depth(s); + break; + case CIRRUS_SR7_BPP_32: + ret = 32; + break; + default: +#ifdef DEBUG_CIRRUS + printf("cirrus: unknown bpp - sr7=%x\n", s->sr[0x7]); +#endif + ret = 8; + break; + } + } else { + /* VGA */ + ret = 0; + } + + return ret; +} + +static void cirrus_get_resolution(VGAState *s, int *pwidth, int *pheight) +{ + int width, height; + + width = (s->cr[0x01] + 1) * 8; + height = s->cr[0x12] | + ((s->cr[0x07] & 0x02) << 7) | + ((s->cr[0x07] & 0x40) << 3); + height = (height + 1); + /* interlace support */ + if (s->cr[0x1a] & 0x01) + height = height * 2; + *pwidth = width; + *pheight = height; +} + +/*************************************** + * + * bank memory + * + ***************************************/ + +static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index) +{ + unsigned offset; + unsigned limit; + + if ((s->gr[0x0b] & 0x01) != 0) /* dual bank */ + offset = s->gr[0x09 + bank_index]; + else /* single bank */ + offset = s->gr[0x09]; + + if ((s->gr[0x0b] & 0x20) != 0) + offset <<= 14; + else + offset <<= 12; + + if (s->real_vram_size <= offset) + limit = 0; + else + limit = s->real_vram_size - offset; + + if (((s->gr[0x0b] & 0x01) == 0) && (bank_index != 0)) { + if (limit > 0x8000) { + offset += 0x8000; + limit -= 0x8000; + } else { + limit = 0; + } + } + + if (limit > 0) { + s->cirrus_bank_base[bank_index] = offset; + s->cirrus_bank_limit[bank_index] = limit; + } else { + s->cirrus_bank_base[bank_index] = 0; + s->cirrus_bank_limit[bank_index] = 0; + } +} + +/*************************************** + * + * I/O access between 0x3c4-0x3c5 + * + ***************************************/ + +static int +cirrus_hook_read_sr(CirrusVGAState * s, unsigned reg_index, int *reg_value) +{ + switch (reg_index) { + case 0x00: // Standard VGA + case 0x01: // Standard VGA + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + return CIRRUS_HOOK_NOT_HANDLED; + case 0x06: // Unlock Cirrus extensions + *reg_value = s->sr[reg_index]; + break; + case 0x10: + case 0x30: + case 0x50: + case 0x70: // Graphics Cursor X + case 0x90: + case 0xb0: + case 0xd0: + case 0xf0: // Graphics Cursor X + *reg_value = s->sr[0x10]; + break; + case 0x11: + case 0x31: + case 0x51: + case 0x71: // Graphics Cursor Y + case 0x91: + case 0xb1: + case 0xd1: + case 0xf1: // Graphics Cursor Y + *reg_value = s->sr[0x11]; + break; + case 0x05: // ??? + case 0x07: // Extended Sequencer Mode + case 0x08: // EEPROM Control + case 0x09: // Scratch Register 0 + case 0x0a: // Scratch Register 1 + case 0x0b: // VCLK 0 + case 0x0c: // VCLK 1 + case 0x0d: // VCLK 2 + case 0x0e: // VCLK 3 + case 0x0f: // DRAM Control + case 0x12: // Graphics Cursor Attribute + case 0x13: // Graphics Cursor Pattern Address + case 0x14: // Scratch Register 2 + case 0x15: // Scratch Register 3 + case 0x16: // Performance Tuning Register + case 0x17: // Configuration Readback and Extended Control + case 0x18: // Signature Generator Control + case 0x19: // Signal Generator Result + case 0x1a: // Signal Generator Result + case 0x1b: // VCLK 0 Denominator & Post + case 0x1c: // VCLK 1 Denominator & Post + case 0x1d: // VCLK 2 Denominator & Post + case 0x1e: // VCLK 3 Denominator & Post + case 0x1f: // BIOS Write Enable and MCLK select +#ifdef DEBUG_CIRRUS + printf("cirrus: handled inport sr_index %02x\n", reg_index); +#endif + *reg_value = s->sr[reg_index]; + break; + default: +#ifdef DEBUG_CIRRUS + printf("cirrus: inport sr_index %02x\n", reg_index); +#endif + *reg_value = 0xff; + break; + } + + return CIRRUS_HOOK_HANDLED; +} + +static int +cirrus_hook_write_sr(CirrusVGAState * s, unsigned reg_index, int reg_value) +{ + switch (reg_index) { + case 0x00: // Standard VGA + case 0x01: // Standard VGA + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + return CIRRUS_HOOK_NOT_HANDLED; + case 0x06: // Unlock Cirrus extensions + reg_value &= 0x17; + if (reg_value == 0x12) { + s->sr[reg_index] = 0x12; + } else { + s->sr[reg_index] = 0x0f; + } + break; + case 0x10: + case 0x30: + case 0x50: + case 0x70: // Graphics Cursor X + case 0x90: + case 0xb0: + case 0xd0: + case 0xf0: // Graphics Cursor X + s->sr[0x10] = reg_value; + s->hw_cursor_x = (reg_value << 3) | (reg_index >> 5); + break; + case 0x11: + case 0x31: + case 0x51: + case 0x71: // Graphics Cursor Y + case 0x91: + case 0xb1: + case 0xd1: + case 0xf1: // Graphics Cursor Y + s->sr[0x11] = reg_value; + s->hw_cursor_y = (reg_value << 3) | (reg_index >> 5); + break; + case 0x07: // Extended Sequencer Mode + case 0x08: // EEPROM Control + case 0x09: // Scratch Register 0 + case 0x0a: // Scratch Register 1 + case 0x0b: // VCLK 0 + case 0x0c: // VCLK 1 + case 0x0d: // VCLK 2 + case 0x0e: // VCLK 3 + case 0x0f: // DRAM Control + case 0x12: // Graphics Cursor Attribute + case 0x13: // Graphics Cursor Pattern Address + case 0x14: // Scratch Register 2 + case 0x15: // Scratch Register 3 + case 0x16: // Performance Tuning Register + case 0x18: // Signature Generator Control + case 0x19: // Signature Generator Result + case 0x1a: // Signature Generator Result + case 0x1b: // VCLK 0 Denominator & Post + case 0x1c: // VCLK 1 Denominator & Post + case 0x1d: // VCLK 2 Denominator & Post + case 0x1e: // VCLK 3 Denominator & Post + case 0x1f: // BIOS Write Enable and MCLK select + s->sr[reg_index] = reg_value; +#ifdef DEBUG_CIRRUS + printf("cirrus: handled outport sr_index %02x, sr_value %02x\n", + reg_index, reg_value); +#endif + break; + case 0x17: // Configuration Readback and Extended Control + s->sr[reg_index] = (s->sr[reg_index] & 0x38) | (reg_value & 0xc7); + cirrus_update_memory_access(s); + break; + default: +#ifdef DEBUG_CIRRUS + printf("cirrus: outport sr_index %02x, sr_value %02x\n", reg_index, + reg_value); +#endif + break; + } + + return CIRRUS_HOOK_HANDLED; +} + +/*************************************** + * + * I/O access at 0x3c6 + * + ***************************************/ + +static void cirrus_read_hidden_dac(CirrusVGAState * s, int *reg_value) +{ + *reg_value = 0xff; + if (++s->cirrus_hidden_dac_lockindex == 5) { + *reg_value = s->cirrus_hidden_dac_data; + s->cirrus_hidden_dac_lockindex = 0; + } +} + +static void cirrus_write_hidden_dac(CirrusVGAState * s, int reg_value) +{ + if (s->cirrus_hidden_dac_lockindex == 4) { + s->cirrus_hidden_dac_data = reg_value; +#if defined(DEBUG_CIRRUS) + printf("cirrus: outport hidden DAC, value %02x\n", reg_value); +#endif + } + s->cirrus_hidden_dac_lockindex = 0; +} + +/*************************************** + * + * I/O access at 0x3c9 + * + ***************************************/ + +static int cirrus_hook_read_palette(CirrusVGAState * s, int *reg_value) +{ + if (!(s->sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) + return CIRRUS_HOOK_NOT_HANDLED; + *reg_value = + s->cirrus_hidden_palette[(s->dac_read_index & 0x0f) * 3 + + s->dac_sub_index]; + if (++s->dac_sub_index == 3) { + s->dac_sub_index = 0; + s->dac_read_index++; + } + return CIRRUS_HOOK_HANDLED; +} + +static int cirrus_hook_write_palette(CirrusVGAState * s, int reg_value) +{ + if (!(s->sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) + return CIRRUS_HOOK_NOT_HANDLED; + s->dac_cache[s->dac_sub_index] = reg_value; + if (++s->dac_sub_index == 3) { + memcpy(&s->cirrus_hidden_palette[(s->dac_write_index & 0x0f) * 3], + s->dac_cache, 3); + /* XXX update cursor */ + s->dac_sub_index = 0; + s->dac_write_index++; + } + return CIRRUS_HOOK_HANDLED; +} + +/*************************************** + * + * I/O access between 0x3ce-0x3cf + * + ***************************************/ + +static int +cirrus_hook_read_gr(CirrusVGAState * s, unsigned reg_index, int *reg_value) +{ + switch (reg_index) { + case 0x00: // Standard VGA, BGCOLOR 0x000000ff + *reg_value = s->cirrus_shadow_gr0; + return CIRRUS_HOOK_HANDLED; + case 0x01: // Standard VGA, FGCOLOR 0x000000ff + *reg_value = s->cirrus_shadow_gr1; + return CIRRUS_HOOK_HANDLED; + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + case 0x06: // Standard VGA + case 0x07: // Standard VGA + case 0x08: // Standard VGA + return CIRRUS_HOOK_NOT_HANDLED; + case 0x05: // Standard VGA, Cirrus extended mode + default: + break; + } + + if (reg_index < 0x3a) { + *reg_value = s->gr[reg_index]; + } else { +#ifdef DEBUG_CIRRUS + printf("cirrus: inport gr_index %02x\n", reg_index); +#endif + *reg_value = 0xff; + } + + return CIRRUS_HOOK_HANDLED; +} + +static int +cirrus_hook_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value) +{ +#if defined(DEBUG_BITBLT) && 0 + printf("gr%02x: %02x\n", reg_index, reg_value); +#endif + switch (reg_index) { + case 0x00: // Standard VGA, BGCOLOR 0x000000ff + s->cirrus_shadow_gr0 = reg_value; + return CIRRUS_HOOK_NOT_HANDLED; + case 0x01: // Standard VGA, FGCOLOR 0x000000ff + s->cirrus_shadow_gr1 = reg_value; + return CIRRUS_HOOK_NOT_HANDLED; + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + case 0x06: // Standard VGA + case 0x07: // Standard VGA + case 0x08: // Standard VGA + return CIRRUS_HOOK_NOT_HANDLED; + case 0x05: // Standard VGA, Cirrus extended mode + s->gr[reg_index] = reg_value & 0x7f; + cirrus_update_memory_access(s); + break; + case 0x09: // bank offset #0 + case 0x0A: // bank offset #1 + s->gr[reg_index] = reg_value; + cirrus_update_bank_ptr(s, 0); + cirrus_update_bank_ptr(s, 1); + break; + case 0x0B: + s->gr[reg_index] = reg_value; + cirrus_update_bank_ptr(s, 0); + cirrus_update_bank_ptr(s, 1); + cirrus_update_memory_access(s); + break; + case 0x10: // BGCOLOR 0x0000ff00 + case 0x11: // FGCOLOR 0x0000ff00 + case 0x12: // BGCOLOR 0x00ff0000 + case 0x13: // FGCOLOR 0x00ff0000 + case 0x14: // BGCOLOR 0xff000000 + case 0x15: // FGCOLOR 0xff000000 + case 0x20: // BLT WIDTH 0x0000ff + case 0x22: // BLT HEIGHT 0x0000ff + case 0x24: // BLT DEST PITCH 0x0000ff + case 0x26: // BLT SRC PITCH 0x0000ff + case 0x28: // BLT DEST ADDR 0x0000ff + case 0x29: // BLT DEST ADDR 0x00ff00 + case 0x2c: // BLT SRC ADDR 0x0000ff + case 0x2d: // BLT SRC ADDR 0x00ff00 + case 0x2f: // BLT WRITEMASK + case 0x30: // BLT MODE + case 0x32: // RASTER OP + case 0x33: // BLT MODEEXT + case 0x34: // BLT TRANSPARENT COLOR 0x00ff + case 0x35: // BLT TRANSPARENT COLOR 0xff00 + case 0x38: // BLT TRANSPARENT COLOR MASK 0x00ff + case 0x39: // BLT TRANSPARENT COLOR MASK 0xff00 + s->gr[reg_index] = reg_value; + break; + case 0x21: // BLT WIDTH 0x001f00 + case 0x23: // BLT HEIGHT 0x001f00 + case 0x25: // BLT DEST PITCH 0x001f00 + case 0x27: // BLT SRC PITCH 0x001f00 + s->gr[reg_index] = reg_value & 0x1f; + break; + case 0x2a: // BLT DEST ADDR 0x3f0000 + s->gr[reg_index] = reg_value & 0x3f; + /* if auto start mode, starts bit blt now */ + if (s->gr[0x31] & CIRRUS_BLT_AUTOSTART) { + cirrus_bitblt_start(s); + } + break; + case 0x2e: // BLT SRC ADDR 0x3f0000 + s->gr[reg_index] = reg_value & 0x3f; + break; + case 0x31: // BLT STATUS/START + cirrus_write_bitblt(s, reg_value); + break; + default: +#ifdef DEBUG_CIRRUS + printf("cirrus: outport gr_index %02x, gr_value %02x\n", reg_index, + reg_value); +#endif + break; + } + + return CIRRUS_HOOK_HANDLED; +} + +/*************************************** + * + * I/O access between 0x3d4-0x3d5 + * + ***************************************/ + +static int +cirrus_hook_read_cr(CirrusVGAState * s, unsigned reg_index, int *reg_value) +{ + switch (reg_index) { + case 0x00: // Standard VGA + case 0x01: // Standard VGA + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + case 0x05: // Standard VGA + case 0x06: // Standard VGA + case 0x07: // Standard VGA + case 0x08: // Standard VGA + case 0x09: // Standard VGA + case 0x0a: // Standard VGA + case 0x0b: // Standard VGA + case 0x0c: // Standard VGA + case 0x0d: // Standard VGA + case 0x0e: // Standard VGA + case 0x0f: // Standard VGA + case 0x10: // Standard VGA + case 0x11: // Standard VGA + case 0x12: // Standard VGA + case 0x13: // Standard VGA + case 0x14: // Standard VGA + case 0x15: // Standard VGA + case 0x16: // Standard VGA + case 0x17: // Standard VGA + case 0x18: // Standard VGA + return CIRRUS_HOOK_NOT_HANDLED; + case 0x19: // Interlace End + case 0x1a: // Miscellaneous Control + case 0x1b: // Extended Display Control + case 0x1c: // Sync Adjust and Genlock + case 0x1d: // Overlay Extended Control + case 0x22: // Graphics Data Latches Readback (R) + case 0x24: // Attribute Controller Toggle Readback (R) + case 0x25: // Part Status + case 0x27: // Part ID (R) + *reg_value = s->cr[reg_index]; + break; + case 0x26: // Attribute Controller Index Readback (R) + *reg_value = s->ar_index & 0x3f; + break; + default: +#ifdef DEBUG_CIRRUS + printf("cirrus: inport cr_index %02x\n", reg_index); + *reg_value = 0xff; +#endif + break; + } + + return CIRRUS_HOOK_HANDLED; +} + +static int +cirrus_hook_write_cr(CirrusVGAState * s, unsigned reg_index, int reg_value) +{ + switch (reg_index) { + case 0x00: // Standard VGA + case 0x01: // Standard VGA + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + case 0x05: // Standard VGA + case 0x06: // Standard VGA + case 0x07: // Standard VGA + case 0x08: // Standard VGA + case 0x09: // Standard VGA + case 0x0a: // Standard VGA + case 0x0b: // Standard VGA + case 0x0c: // Standard VGA + case 0x0d: // Standard VGA + case 0x0e: // Standard VGA + case 0x0f: // Standard VGA + case 0x10: // Standard VGA + case 0x11: // Standard VGA + case 0x12: // Standard VGA + case 0x13: // Standard VGA + case 0x14: // Standard VGA + case 0x15: // Standard VGA + case 0x16: // Standard VGA + case 0x17: // Standard VGA + case 0x18: // Standard VGA + return CIRRUS_HOOK_NOT_HANDLED; + case 0x19: // Interlace End + case 0x1a: // Miscellaneous Control + case 0x1b: // Extended Display Control + case 0x1c: // Sync Adjust and Genlock + case 0x1d: // Overlay Extended Control + s->cr[reg_index] = reg_value; +#ifdef DEBUG_CIRRUS + printf("cirrus: handled outport cr_index %02x, cr_value %02x\n", + reg_index, reg_value); +#endif + break; + case 0x22: // Graphics Data Latches Readback (R) + case 0x24: // Attribute Controller Toggle Readback (R) + case 0x26: // Attribute Controller Index Readback (R) + case 0x27: // Part ID (R) + break; + case 0x25: // Part Status + default: +#ifdef DEBUG_CIRRUS + printf("cirrus: outport cr_index %02x, cr_value %02x\n", reg_index, + reg_value); +#endif + break; + } + + return CIRRUS_HOOK_HANDLED; +} + +/*************************************** + * + * memory-mapped I/O (bitblt) + * + ***************************************/ + +static uint8_t cirrus_mmio_blt_read(CirrusVGAState * s, unsigned address) +{ + int value = 0xff; + + switch (address) { + case (CIRRUS_MMIO_BLTBGCOLOR + 0): + cirrus_hook_read_gr(s, 0x00, &value); + break; + case (CIRRUS_MMIO_BLTBGCOLOR + 1): + cirrus_hook_read_gr(s, 0x10, &value); + break; + case (CIRRUS_MMIO_BLTBGCOLOR + 2): + cirrus_hook_read_gr(s, 0x12, &value); + break; + case (CIRRUS_MMIO_BLTBGCOLOR + 3): + cirrus_hook_read_gr(s, 0x14, &value); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 0): + cirrus_hook_read_gr(s, 0x01, &value); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 1): + cirrus_hook_read_gr(s, 0x11, &value); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 2): + cirrus_hook_read_gr(s, 0x13, &value); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 3): + cirrus_hook_read_gr(s, 0x15, &value); + break; + case (CIRRUS_MMIO_BLTWIDTH + 0): + cirrus_hook_read_gr(s, 0x20, &value); + break; + case (CIRRUS_MMIO_BLTWIDTH + 1): + cirrus_hook_read_gr(s, 0x21, &value); + break; + case (CIRRUS_MMIO_BLTHEIGHT + 0): + cirrus_hook_read_gr(s, 0x22, &value); + break; + case (CIRRUS_MMIO_BLTHEIGHT + 1): + cirrus_hook_read_gr(s, 0x23, &value); + break; + case (CIRRUS_MMIO_BLTDESTPITCH + 0): + cirrus_hook_read_gr(s, 0x24, &value); + break; + case (CIRRUS_MMIO_BLTDESTPITCH + 1): + cirrus_hook_read_gr(s, 0x25, &value); + break; + case (CIRRUS_MMIO_BLTSRCPITCH + 0): + cirrus_hook_read_gr(s, 0x26, &value); + break; + case (CIRRUS_MMIO_BLTSRCPITCH + 1): + cirrus_hook_read_gr(s, 0x27, &value); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 0): + cirrus_hook_read_gr(s, 0x28, &value); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 1): + cirrus_hook_read_gr(s, 0x29, &value); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 2): + cirrus_hook_read_gr(s, 0x2a, &value); + break; + case (CIRRUS_MMIO_BLTSRCADDR + 0): + cirrus_hook_read_gr(s, 0x2c, &value); + break; + case (CIRRUS_MMIO_BLTSRCADDR + 1): + cirrus_hook_read_gr(s, 0x2d, &value); + break; + case (CIRRUS_MMIO_BLTSRCADDR + 2): + cirrus_hook_read_gr(s, 0x2e, &value); + break; + case CIRRUS_MMIO_BLTWRITEMASK: + cirrus_hook_read_gr(s, 0x2f, &value); + break; + case CIRRUS_MMIO_BLTMODE: + cirrus_hook_read_gr(s, 0x30, &value); + break; + case CIRRUS_MMIO_BLTROP: + cirrus_hook_read_gr(s, 0x32, &value); + break; + case CIRRUS_MMIO_BLTMODEEXT: + cirrus_hook_read_gr(s, 0x33, &value); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0): + cirrus_hook_read_gr(s, 0x34, &value); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1): + cirrus_hook_read_gr(s, 0x35, &value); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0): + cirrus_hook_read_gr(s, 0x38, &value); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1): + cirrus_hook_read_gr(s, 0x39, &value); + break; + case CIRRUS_MMIO_BLTSTATUS: + cirrus_hook_read_gr(s, 0x31, &value); + break; + default: +#ifdef DEBUG_CIRRUS + printf("cirrus: mmio read - address 0x%04x\n", address); +#endif + break; + } + + return (uint8_t) value; +} + +static void cirrus_mmio_blt_write(CirrusVGAState * s, unsigned address, + uint8_t value) +{ + switch (address) { + case (CIRRUS_MMIO_BLTBGCOLOR + 0): + cirrus_hook_write_gr(s, 0x00, value); + break; + case (CIRRUS_MMIO_BLTBGCOLOR + 1): + cirrus_hook_write_gr(s, 0x10, value); + break; + case (CIRRUS_MMIO_BLTBGCOLOR + 2): + cirrus_hook_write_gr(s, 0x12, value); + break; + case (CIRRUS_MMIO_BLTBGCOLOR + 3): + cirrus_hook_write_gr(s, 0x14, value); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 0): + cirrus_hook_write_gr(s, 0x01, value); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 1): + cirrus_hook_write_gr(s, 0x11, value); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 2): + cirrus_hook_write_gr(s, 0x13, value); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 3): + cirrus_hook_write_gr(s, 0x15, value); + break; + case (CIRRUS_MMIO_BLTWIDTH + 0): + cirrus_hook_write_gr(s, 0x20, value); + break; + case (CIRRUS_MMIO_BLTWIDTH + 1): + cirrus_hook_write_gr(s, 0x21, value); + break; + case (CIRRUS_MMIO_BLTHEIGHT + 0): + cirrus_hook_write_gr(s, 0x22, value); + break; + case (CIRRUS_MMIO_BLTHEIGHT + 1): + cirrus_hook_write_gr(s, 0x23, value); + break; + case (CIRRUS_MMIO_BLTDESTPITCH + 0): + cirrus_hook_write_gr(s, 0x24, value); + break; + case (CIRRUS_MMIO_BLTDESTPITCH + 1): + cirrus_hook_write_gr(s, 0x25, value); + break; + case (CIRRUS_MMIO_BLTSRCPITCH + 0): + cirrus_hook_write_gr(s, 0x26, value); + break; + case (CIRRUS_MMIO_BLTSRCPITCH + 1): + cirrus_hook_write_gr(s, 0x27, value); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 0): + cirrus_hook_write_gr(s, 0x28, value); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 1): + cirrus_hook_write_gr(s, 0x29, value); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 2): + cirrus_hook_write_gr(s, 0x2a, value); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 3): + /* ignored */ + break; + case (CIRRUS_MMIO_BLTSRCADDR + 0): + cirrus_hook_write_gr(s, 0x2c, value); + break; + case (CIRRUS_MMIO_BLTSRCADDR + 1): + cirrus_hook_write_gr(s, 0x2d, value); + break; + case (CIRRUS_MMIO_BLTSRCADDR + 2): + cirrus_hook_write_gr(s, 0x2e, value); + break; + case CIRRUS_MMIO_BLTWRITEMASK: + cirrus_hook_write_gr(s, 0x2f, value); + break; + case CIRRUS_MMIO_BLTMODE: + cirrus_hook_write_gr(s, 0x30, value); + break; + case CIRRUS_MMIO_BLTROP: + cirrus_hook_write_gr(s, 0x32, value); + break; + case CIRRUS_MMIO_BLTMODEEXT: + cirrus_hook_write_gr(s, 0x33, value); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0): + cirrus_hook_write_gr(s, 0x34, value); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1): + cirrus_hook_write_gr(s, 0x35, value); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0): + cirrus_hook_write_gr(s, 0x38, value); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1): + cirrus_hook_write_gr(s, 0x39, value); + break; + case CIRRUS_MMIO_BLTSTATUS: + cirrus_hook_write_gr(s, 0x31, value); + break; + default: +#ifdef DEBUG_CIRRUS + printf("cirrus: mmio write - addr 0x%04x val 0x%02x (ignored)\n", + address, value); +#endif + break; + } +} + +/*************************************** + * + * write mode 4/5 + * + * assume TARGET_PAGE_SIZE >= 16 + * + ***************************************/ + +static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s, + unsigned mode, + unsigned offset, + uint32_t mem_value) +{ + int x; + unsigned val = mem_value; + uint8_t *dst; + + dst = s->vram_ptr + offset; + for (x = 0; x < 8; x++) { + if (val & 0x80) { + *dst = s->cirrus_shadow_gr1; + } else if (mode == 5) { + *dst = s->cirrus_shadow_gr0; + } + val <<= 1; + dst++; + } + cpu_physical_memory_set_dirty(s->vram_offset + offset); + cpu_physical_memory_set_dirty(s->vram_offset + offset + 7); +} + +static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s, + unsigned mode, + unsigned offset, + uint32_t mem_value) +{ + int x; + unsigned val = mem_value; + uint8_t *dst; + + dst = s->vram_ptr + offset; + for (x = 0; x < 8; x++) { + if (val & 0x80) { + *dst = s->cirrus_shadow_gr1; + *(dst + 1) = s->gr[0x11]; + } else if (mode == 5) { + *dst = s->cirrus_shadow_gr0; + *(dst + 1) = s->gr[0x10]; + } + val <<= 1; + dst += 2; + } + cpu_physical_memory_set_dirty(s->vram_offset + offset); + cpu_physical_memory_set_dirty(s->vram_offset + offset + 15); +} + +/*************************************** + * + * memory access between 0xa0000-0xbffff + * + ***************************************/ + +static uint32_t cirrus_vga_mem_readb(void *opaque, target_phys_addr_t addr) +{ + CirrusVGAState *s = opaque; + unsigned bank_index; + unsigned bank_offset; + uint32_t val; + + if ((s->sr[0x07] & 0x01) == 0) { + return vga_mem_readb(s, addr); + } + + addr &= 0x1ffff; + + if (addr < 0x10000) { + /* XXX handle bitblt */ + /* video memory */ + bank_index = addr >> 15; + bank_offset = addr & 0x7fff; + if (bank_offset < s->cirrus_bank_limit[bank_index]) { + bank_offset += s->cirrus_bank_base[bank_index]; + if ((s->gr[0x0B] & 0x14) == 0x14) { + bank_offset <<= 4; + } else if (s->gr[0x0B] & 0x02) { + bank_offset <<= 3; + } + bank_offset &= s->cirrus_addr_mask; + val = *(s->vram_ptr + bank_offset); + } else + val = 0xff; + } else if (addr >= 0x18000 && addr < 0x18100) { + /* memory-mapped I/O */ + val = 0xff; + if ((s->sr[0x17] & 0x44) == 0x04) { + val = cirrus_mmio_blt_read(s, addr & 0xff); + } + } else { + val = 0xff; +#ifdef DEBUG_CIRRUS + printf("cirrus: mem_readb %06x\n", addr); +#endif + } + return val; +} + +static uint32_t cirrus_vga_mem_readw(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; +#ifdef TARGET_WORDS_BIGENDIAN + v = cirrus_vga_mem_readb(opaque, addr) << 8; + v |= cirrus_vga_mem_readb(opaque, addr + 1); +#else + v = cirrus_vga_mem_readb(opaque, addr); + v |= cirrus_vga_mem_readb(opaque, addr + 1) << 8; +#endif + return v; +} + +static uint32_t cirrus_vga_mem_readl(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; +#ifdef TARGET_WORDS_BIGENDIAN + v = cirrus_vga_mem_readb(opaque, addr) << 24; + v |= cirrus_vga_mem_readb(opaque, addr + 1) << 16; + v |= cirrus_vga_mem_readb(opaque, addr + 2) << 8; + v |= cirrus_vga_mem_readb(opaque, addr + 3); +#else + v = cirrus_vga_mem_readb(opaque, addr); + v |= cirrus_vga_mem_readb(opaque, addr + 1) << 8; + v |= cirrus_vga_mem_readb(opaque, addr + 2) << 16; + v |= cirrus_vga_mem_readb(opaque, addr + 3) << 24; +#endif + return v; +} + +static void cirrus_vga_mem_writeb(void *opaque, target_phys_addr_t addr, + uint32_t mem_value) +{ + CirrusVGAState *s = opaque; + unsigned bank_index; + unsigned bank_offset; + unsigned mode; + + if ((s->sr[0x07] & 0x01) == 0) { + vga_mem_writeb(s, addr, mem_value); + return; + } + + addr &= 0x1ffff; + + if (addr < 0x10000) { + if (s->cirrus_srcptr != s->cirrus_srcptr_end) { + /* bitblt */ + *s->cirrus_srcptr++ = (uint8_t) mem_value; + if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { + cirrus_bitblt_cputovideo_next(s); + } + } else { + /* video memory */ + bank_index = addr >> 15; + bank_offset = addr & 0x7fff; + if (bank_offset < s->cirrus_bank_limit[bank_index]) { + bank_offset += s->cirrus_bank_base[bank_index]; + if ((s->gr[0x0B] & 0x14) == 0x14) { + bank_offset <<= 4; + } else if (s->gr[0x0B] & 0x02) { + bank_offset <<= 3; + } + bank_offset &= s->cirrus_addr_mask; + mode = s->gr[0x05] & 0x7; + if (mode < 4 || mode > 5 || ((s->gr[0x0B] & 0x4) == 0)) { + *(s->vram_ptr + bank_offset) = mem_value; + cpu_physical_memory_set_dirty(s->vram_offset + + bank_offset); + } else { + if ((s->gr[0x0B] & 0x14) != 0x14) { + cirrus_mem_writeb_mode4and5_8bpp(s, mode, + bank_offset, + mem_value); + } else { + cirrus_mem_writeb_mode4and5_16bpp(s, mode, + bank_offset, + mem_value); + } + } + } + } + } else if (addr >= 0x18000 && addr < 0x18100) { + /* memory-mapped I/O */ + if ((s->sr[0x17] & 0x44) == 0x04) { + cirrus_mmio_blt_write(s, addr & 0xff, mem_value); + } + } else { +#ifdef DEBUG_CIRRUS + printf("cirrus: mem_writeb %06x value %02x\n", addr, mem_value); +#endif + } +} + +static void cirrus_vga_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ +#ifdef TARGET_WORDS_BIGENDIAN + cirrus_vga_mem_writeb(opaque, addr, (val >> 8) & 0xff); + cirrus_vga_mem_writeb(opaque, addr + 1, val & 0xff); +#else + cirrus_vga_mem_writeb(opaque, addr, val & 0xff); + cirrus_vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff); +#endif +} + +static void cirrus_vga_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ +#ifdef TARGET_WORDS_BIGENDIAN + cirrus_vga_mem_writeb(opaque, addr, (val >> 24) & 0xff); + cirrus_vga_mem_writeb(opaque, addr + 1, (val >> 16) & 0xff); + cirrus_vga_mem_writeb(opaque, addr + 2, (val >> 8) & 0xff); + cirrus_vga_mem_writeb(opaque, addr + 3, val & 0xff); +#else + cirrus_vga_mem_writeb(opaque, addr, val & 0xff); + cirrus_vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff); + cirrus_vga_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff); + cirrus_vga_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff); +#endif +} + +static CPUReadMemoryFunc *cirrus_vga_mem_read[3] = { + cirrus_vga_mem_readb, + cirrus_vga_mem_readw, + cirrus_vga_mem_readl, +}; + +static CPUWriteMemoryFunc *cirrus_vga_mem_write[3] = { + cirrus_vga_mem_writeb, + cirrus_vga_mem_writew, + cirrus_vga_mem_writel, +}; + +/*************************************** + * + * hardware cursor + * + ***************************************/ + +static inline void invalidate_cursor1(CirrusVGAState *s) +{ + if (s->last_hw_cursor_size) { + vga_invalidate_scanlines((VGAState *)s, + s->last_hw_cursor_y + s->last_hw_cursor_y_start, + s->last_hw_cursor_y + s->last_hw_cursor_y_end); + } +} + +static inline void cirrus_cursor_compute_yrange(CirrusVGAState *s) +{ + const uint8_t *src; + uint32_t content; + int y, y_min, y_max; + + src = s->vram_ptr + s->real_vram_size - 16 * 1024; + if (s->sr[0x12] & CIRRUS_CURSOR_LARGE) { + src += (s->sr[0x13] & 0x3c) * 256; + y_min = 64; + y_max = -1; + for(y = 0; y < 64; y++) { + content = ((uint32_t *)src)[0] | + ((uint32_t *)src)[1] | + ((uint32_t *)src)[2] | + ((uint32_t *)src)[3]; + if (content) { + if (y < y_min) + y_min = y; + if (y > y_max) + y_max = y; + } + src += 16; + } + } else { + src += (s->sr[0x13] & 0x3f) * 256; + y_min = 32; + y_max = -1; + for(y = 0; y < 32; y++) { + content = ((uint32_t *)src)[0] | + ((uint32_t *)(src + 128))[0]; + if (content) { + if (y < y_min) + y_min = y; + if (y > y_max) + y_max = y; + } + src += 4; + } + } + if (y_min > y_max) { + s->last_hw_cursor_y_start = 0; + s->last_hw_cursor_y_end = 0; + } else { + s->last_hw_cursor_y_start = y_min; + s->last_hw_cursor_y_end = y_max + 1; + } +} + +/* NOTE: we do not currently handle the cursor bitmap change, so we + update the cursor only if it moves. */ +static void cirrus_cursor_invalidate(VGAState *s1) +{ + CirrusVGAState *s = (CirrusVGAState *)s1; + int size; + + if (!s->sr[0x12] & CIRRUS_CURSOR_SHOW) { + size = 0; + } else { + if (s->sr[0x12] & CIRRUS_CURSOR_LARGE) + size = 64; + else + size = 32; + } + /* invalidate last cursor and new cursor if any change */ + if (s->last_hw_cursor_size != size || + s->last_hw_cursor_x != s->hw_cursor_x || + s->last_hw_cursor_y != s->hw_cursor_y) { + + invalidate_cursor1(s); + + s->last_hw_cursor_size = size; + s->last_hw_cursor_x = s->hw_cursor_x; + s->last_hw_cursor_y = s->hw_cursor_y; + /* compute the real cursor min and max y */ + cirrus_cursor_compute_yrange(s); + invalidate_cursor1(s); + } +} + +static void cirrus_cursor_draw_line(VGAState *s1, uint8_t *d1, int scr_y) +{ + CirrusVGAState *s = (CirrusVGAState *)s1; + int w, h, bpp, x1, x2, poffset; + unsigned int color0, color1; + const uint8_t *palette, *src; + uint32_t content; + + if (!(s->sr[0x12] & CIRRUS_CURSOR_SHOW)) + return; + /* fast test to see if the cursor intersects with the scan line */ + if (s->sr[0x12] & CIRRUS_CURSOR_LARGE) { + h = 64; + } else { + h = 32; + } + if (scr_y < s->hw_cursor_y || + scr_y >= (s->hw_cursor_y + h)) + return; + + src = s->vram_ptr + s->real_vram_size - 16 * 1024; + if (s->sr[0x12] & CIRRUS_CURSOR_LARGE) { + src += (s->sr[0x13] & 0x3c) * 256; + src += (scr_y - s->hw_cursor_y) * 16; + poffset = 8; + content = ((uint32_t *)src)[0] | + ((uint32_t *)src)[1] | + ((uint32_t *)src)[2] | + ((uint32_t *)src)[3]; + } else { + src += (s->sr[0x13] & 0x3f) * 256; + src += (scr_y - s->hw_cursor_y) * 4; + poffset = 128; + content = ((uint32_t *)src)[0] | + ((uint32_t *)(src + 128))[0]; + } + /* if nothing to draw, no need to continue */ + if (!content) + return; + w = h; + + x1 = s->hw_cursor_x; + if (x1 >= s->last_scr_width) + return; + x2 = s->hw_cursor_x + w; + if (x2 > s->last_scr_width) + x2 = s->last_scr_width; + w = x2 - x1; + palette = s->cirrus_hidden_palette; + color0 = s->rgb_to_pixel(c6_to_8(palette[0x0 * 3]), + c6_to_8(palette[0x0 * 3 + 1]), + c6_to_8(palette[0x0 * 3 + 2])); + color1 = s->rgb_to_pixel(c6_to_8(palette[0xf * 3]), + c6_to_8(palette[0xf * 3 + 1]), + c6_to_8(palette[0xf * 3 + 2])); + bpp = ((s->ds->depth + 7) >> 3); + d1 += x1 * bpp; + switch(s->ds->depth) { + default: + break; + case 8: + vga_draw_cursor_line_8(d1, src, poffset, w, color0, color1, 0xff); + break; + case 15: + vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0x7fff); + break; + case 16: + vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0xffff); + break; + case 32: + vga_draw_cursor_line_32(d1, src, poffset, w, color0, color1, 0xffffff); + break; + } +} + +/*************************************** + * + * LFB memory access + * + ***************************************/ + +static uint32_t cirrus_linear_readb(void *opaque, target_phys_addr_t addr) +{ + CirrusVGAState *s = (CirrusVGAState *) opaque; + uint32_t ret; + + addr &= s->cirrus_addr_mask; + + if (((s->sr[0x17] & 0x44) == 0x44) && + ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) { + /* memory-mapped I/O */ + ret = cirrus_mmio_blt_read(s, addr & 0xff); + } else if (0) { + /* XXX handle bitblt */ + ret = 0xff; + } else { + /* video memory */ + if ((s->gr[0x0B] & 0x14) == 0x14) { + addr <<= 4; + } else if (s->gr[0x0B] & 0x02) { + addr <<= 3; + } + addr &= s->cirrus_addr_mask; + ret = *(s->vram_ptr + addr); + } + + return ret; +} + +static uint32_t cirrus_linear_readw(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; +#ifdef TARGET_WORDS_BIGENDIAN + v = cirrus_linear_readb(opaque, addr) << 8; + v |= cirrus_linear_readb(opaque, addr + 1); +#else + v = cirrus_linear_readb(opaque, addr); + v |= cirrus_linear_readb(opaque, addr + 1) << 8; +#endif + return v; +} + +static uint32_t cirrus_linear_readl(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; +#ifdef TARGET_WORDS_BIGENDIAN + v = cirrus_linear_readb(opaque, addr) << 24; + v |= cirrus_linear_readb(opaque, addr + 1) << 16; + v |= cirrus_linear_readb(opaque, addr + 2) << 8; + v |= cirrus_linear_readb(opaque, addr + 3); +#else + v = cirrus_linear_readb(opaque, addr); + v |= cirrus_linear_readb(opaque, addr + 1) << 8; + v |= cirrus_linear_readb(opaque, addr + 2) << 16; + v |= cirrus_linear_readb(opaque, addr + 3) << 24; +#endif + return v; +} + +static void cirrus_linear_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + CirrusVGAState *s = (CirrusVGAState *) opaque; + unsigned mode; + + addr &= s->cirrus_addr_mask; + + if (((s->sr[0x17] & 0x44) == 0x44) && + ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) { + /* memory-mapped I/O */ + cirrus_mmio_blt_write(s, addr & 0xff, val); + } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) { + /* bitblt */ + *s->cirrus_srcptr++ = (uint8_t) val; + if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { + cirrus_bitblt_cputovideo_next(s); + } + } else { + /* video memory */ + if ((s->gr[0x0B] & 0x14) == 0x14) { + addr <<= 4; + } else if (s->gr[0x0B] & 0x02) { + addr <<= 3; + } + addr &= s->cirrus_addr_mask; + + mode = s->gr[0x05] & 0x7; + if (mode < 4 || mode > 5 || ((s->gr[0x0B] & 0x4) == 0)) { + *(s->vram_ptr + addr) = (uint8_t) val; + cpu_physical_memory_set_dirty(s->vram_offset + addr); + } else { + if ((s->gr[0x0B] & 0x14) != 0x14) { + cirrus_mem_writeb_mode4and5_8bpp(s, mode, addr, val); + } else { + cirrus_mem_writeb_mode4and5_16bpp(s, mode, addr, val); + } + } + } +} + +static void cirrus_linear_writew(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ +#ifdef TARGET_WORDS_BIGENDIAN + cirrus_linear_writeb(opaque, addr, (val >> 8) & 0xff); + cirrus_linear_writeb(opaque, addr + 1, val & 0xff); +#else + cirrus_linear_writeb(opaque, addr, val & 0xff); + cirrus_linear_writeb(opaque, addr + 1, (val >> 8) & 0xff); +#endif +} + +static void cirrus_linear_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ +#ifdef TARGET_WORDS_BIGENDIAN + cirrus_linear_writeb(opaque, addr, (val >> 24) & 0xff); + cirrus_linear_writeb(opaque, addr + 1, (val >> 16) & 0xff); + cirrus_linear_writeb(opaque, addr + 2, (val >> 8) & 0xff); + cirrus_linear_writeb(opaque, addr + 3, val & 0xff); +#else + cirrus_linear_writeb(opaque, addr, val & 0xff); + cirrus_linear_writeb(opaque, addr + 1, (val >> 8) & 0xff); + cirrus_linear_writeb(opaque, addr + 2, (val >> 16) & 0xff); + cirrus_linear_writeb(opaque, addr + 3, (val >> 24) & 0xff); +#endif +} + + +static CPUReadMemoryFunc *cirrus_linear_read[3] = { + cirrus_linear_readb, + cirrus_linear_readw, + cirrus_linear_readl, +}; + +static CPUWriteMemoryFunc *cirrus_linear_write[3] = { + cirrus_linear_writeb, + cirrus_linear_writew, + cirrus_linear_writel, +}; + +static void cirrus_linear_mem_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + CirrusVGAState *s = (CirrusVGAState *) opaque; + + addr &= s->cirrus_addr_mask; + *(s->vram_ptr + addr) = val; + cpu_physical_memory_set_dirty(s->vram_offset + addr); +} + +static void cirrus_linear_mem_writew(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + CirrusVGAState *s = (CirrusVGAState *) opaque; + + addr &= s->cirrus_addr_mask; + cpu_to_le16w((uint16_t *)(s->vram_ptr + addr), val); + cpu_physical_memory_set_dirty(s->vram_offset + addr); +} + +static void cirrus_linear_mem_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + CirrusVGAState *s = (CirrusVGAState *) opaque; + + addr &= s->cirrus_addr_mask; + cpu_to_le32w((uint32_t *)(s->vram_ptr + addr), val); + cpu_physical_memory_set_dirty(s->vram_offset + addr); +} + +/*************************************** + * + * system to screen memory access + * + ***************************************/ + + +static uint32_t cirrus_linear_bitblt_readb(void *opaque, target_phys_addr_t addr) +{ + uint32_t ret; + + /* XXX handle bitblt */ + ret = 0xff; + return ret; +} + +static uint32_t cirrus_linear_bitblt_readw(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; +#ifdef TARGET_WORDS_BIGENDIAN + v = cirrus_linear_bitblt_readb(opaque, addr) << 8; + v |= cirrus_linear_bitblt_readb(opaque, addr + 1); +#else + v = cirrus_linear_bitblt_readb(opaque, addr); + v |= cirrus_linear_bitblt_readb(opaque, addr + 1) << 8; +#endif + return v; +} + +static uint32_t cirrus_linear_bitblt_readl(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; +#ifdef TARGET_WORDS_BIGENDIAN + v = cirrus_linear_bitblt_readb(opaque, addr) << 24; + v |= cirrus_linear_bitblt_readb(opaque, addr + 1) << 16; + v |= cirrus_linear_bitblt_readb(opaque, addr + 2) << 8; + v |= cirrus_linear_bitblt_readb(opaque, addr + 3); +#else + v = cirrus_linear_bitblt_readb(opaque, addr); + v |= cirrus_linear_bitblt_readb(opaque, addr + 1) << 8; + v |= cirrus_linear_bitblt_readb(opaque, addr + 2) << 16; + v |= cirrus_linear_bitblt_readb(opaque, addr + 3) << 24; +#endif + return v; +} + +static void cirrus_linear_bitblt_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + CirrusVGAState *s = (CirrusVGAState *) opaque; + + if (s->cirrus_srcptr != s->cirrus_srcptr_end) { + /* bitblt */ + *s->cirrus_srcptr++ = (uint8_t) val; + if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { + cirrus_bitblt_cputovideo_next(s); + } + } +} + +static void cirrus_linear_bitblt_writew(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ +#ifdef TARGET_WORDS_BIGENDIAN + cirrus_linear_bitblt_writeb(opaque, addr, (val >> 8) & 0xff); + cirrus_linear_bitblt_writeb(opaque, addr + 1, val & 0xff); +#else + cirrus_linear_bitblt_writeb(opaque, addr, val & 0xff); + cirrus_linear_bitblt_writeb(opaque, addr + 1, (val >> 8) & 0xff); +#endif +} + +static void cirrus_linear_bitblt_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ +#ifdef TARGET_WORDS_BIGENDIAN + cirrus_linear_bitblt_writeb(opaque, addr, (val >> 24) & 0xff); + cirrus_linear_bitblt_writeb(opaque, addr + 1, (val >> 16) & 0xff); + cirrus_linear_bitblt_writeb(opaque, addr + 2, (val >> 8) & 0xff); + cirrus_linear_bitblt_writeb(opaque, addr + 3, val & 0xff); +#else + cirrus_linear_bitblt_writeb(opaque, addr, val & 0xff); + cirrus_linear_bitblt_writeb(opaque, addr + 1, (val >> 8) & 0xff); + cirrus_linear_bitblt_writeb(opaque, addr + 2, (val >> 16) & 0xff); + cirrus_linear_bitblt_writeb(opaque, addr + 3, (val >> 24) & 0xff); +#endif +} + + +static CPUReadMemoryFunc *cirrus_linear_bitblt_read[3] = { + cirrus_linear_bitblt_readb, + cirrus_linear_bitblt_readw, + cirrus_linear_bitblt_readl, +}; + +static CPUWriteMemoryFunc *cirrus_linear_bitblt_write[3] = { + cirrus_linear_bitblt_writeb, + cirrus_linear_bitblt_writew, + cirrus_linear_bitblt_writel, +}; + +static void *set_vram_mapping(unsigned long begin, unsigned long end) +{ + xen_pfn_t *extent_start = NULL; + unsigned long nr_extents; + void *vram_pointer = NULL; + int i; + + /* align begin and end address */ + begin = begin & TARGET_PAGE_MASK; + end = begin + VGA_RAM_SIZE; + end = (end + TARGET_PAGE_SIZE -1 ) & TARGET_PAGE_MASK; + nr_extents = (end - begin) >> TARGET_PAGE_BITS; + + extent_start = malloc(sizeof(xen_pfn_t) * nr_extents); + if (extent_start == NULL) { + fprintf(stderr, "Failed malloc on set_vram_mapping\n"); + return NULL; + } + + memset(extent_start, 0, sizeof(xen_pfn_t) * nr_extents); + + for (i = 0; i < nr_extents; i++) + extent_start[i] = (begin + i * TARGET_PAGE_SIZE) >> TARGET_PAGE_BITS; + + set_mm_mapping(xc_handle, domid, nr_extents, 0, extent_start); + + vram_pointer = xc_map_foreign_batch(xc_handle, domid, + PROT_READ|PROT_WRITE, + extent_start, nr_extents); + if (vram_pointer == NULL) { + fprintf(logfile, "xc_map_foreign_batch vgaram returned error %d\n", + errno); + return NULL; + } + + memset(vram_pointer, 0, nr_extents * TARGET_PAGE_SIZE); + + free(extent_start); + + return vram_pointer; +} + +static int unset_vram_mapping(unsigned long begin, unsigned long end) +{ + xen_pfn_t *extent_start = NULL; + unsigned long nr_extents; + int i; + + /* align begin and end address */ + + end = begin + VGA_RAM_SIZE; + begin = begin & TARGET_PAGE_MASK; + end = (end + TARGET_PAGE_SIZE -1 ) & TARGET_PAGE_MASK; + nr_extents = (end - begin) >> TARGET_PAGE_BITS; + + extent_start = malloc(sizeof(xen_pfn_t) * nr_extents); + + if (extent_start == NULL) { + fprintf(stderr, "Failed malloc on set_mm_mapping\n"); + return -1; + } + + memset(extent_start, 0, sizeof(xen_pfn_t) * nr_extents); + + for (i = 0; i < nr_extents; i++) + extent_start[i] = (begin + (i * TARGET_PAGE_SIZE)) >> TARGET_PAGE_BITS; + + unset_mm_mapping(xc_handle, domid, nr_extents, 0, extent_start); + + free(extent_start); + + return 0; +} + +/* Compute the memory access functions */ +static void cirrus_update_memory_access(CirrusVGAState *s) +{ + unsigned mode; + + if ((s->sr[0x17] & 0x44) == 0x44) { + goto generic_io; + } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) { + goto generic_io; + } else { + if ((s->gr[0x0B] & 0x14) == 0x14) { + goto generic_io; + } else if (s->gr[0x0B] & 0x02) { + goto generic_io; + } + + mode = s->gr[0x05] & 0x7; + if (mode < 4 || mode > 5 || ((s->gr[0x0B] & 0x4) == 0)) { + if (s->cirrus_lfb_addr && s->cirrus_lfb_end && !s->map_addr) { + void *vram_pointer, *old_vram; + + vram_pointer = set_vram_mapping(s->cirrus_lfb_addr, + s->cirrus_lfb_end); + if (!vram_pointer) + fprintf(stderr, "NULL vram_pointer\n"); + else { + old_vram = vga_update_vram((VGAState *)s, vram_pointer, + VGA_RAM_SIZE); + qemu_free(old_vram); + } + s->map_addr = s->cirrus_lfb_addr; + s->map_end = s->cirrus_lfb_end; + } + s->cirrus_linear_write[0] = cirrus_linear_mem_writeb; + s->cirrus_linear_write[1] = cirrus_linear_mem_writew; + s->cirrus_linear_write[2] = cirrus_linear_mem_writel; + } else { + generic_io: + if (s->cirrus_lfb_addr && s->cirrus_lfb_end && s->map_addr) { + int error; + void *old_vram = NULL; + + error = unset_vram_mapping(s->cirrus_lfb_addr, + s->cirrus_lfb_end); + if (!error) + old_vram = vga_update_vram((VGAState *)s, NULL, + VGA_RAM_SIZE); + if (old_vram) + munmap(old_vram, s->map_addr - s->map_end); + s->map_addr = s->map_end = 0; + } + s->cirrus_linear_write[0] = cirrus_linear_writeb; + s->cirrus_linear_write[1] = cirrus_linear_writew; + s->cirrus_linear_write[2] = cirrus_linear_writel; + } + } +} + + +/* I/O ports */ + +static uint32_t vga_ioport_read(void *opaque, uint32_t addr) +{ + CirrusVGAState *s = opaque; + int val, index; + + /* check port range access depending on color/monochrome mode */ + if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) + || (addr >= 0x3d0 && addr <= 0x3df + && !(s->msr & MSR_COLOR_EMULATION))) { + val = 0xff; + } else { + switch (addr) { + case 0x3c0: + if (s->ar_flip_flop == 0) { + val = s->ar_index; + } else { + val = 0; + } + break; + case 0x3c1: + index = s->ar_index & 0x1f; + if (index < 21) + val = s->ar[index]; + else + val = 0; + break; + case 0x3c2: + val = s->st00; + break; + case 0x3c4: + val = s->sr_index; + break; + case 0x3c5: + if (cirrus_hook_read_sr(s, s->sr_index, &val)) + break; + val = s->sr[s->sr_index]; +#ifdef DEBUG_VGA_REG + printf("vga: read SR%x = 0x%02x\n", s->sr_index, val); +#endif + break; + case 0x3c6: + cirrus_read_hidden_dac(s, &val); + break; + case 0x3c7: + val = s->dac_state; + break; + case 0x3c8: + val = s->dac_write_index; + s->cirrus_hidden_dac_lockindex = 0; + break; + case 0x3c9: + if (cirrus_hook_read_palette(s, &val)) + break; + val = s->palette[s->dac_read_index * 3 + s->dac_sub_index]; + if (++s->dac_sub_index == 3) { + s->dac_sub_index = 0; + s->dac_read_index++; + } + break; + case 0x3ca: + val = s->fcr; + break; + case 0x3cc: + val = s->msr; + break; + case 0x3ce: + val = s->gr_index; + break; + case 0x3cf: + if (cirrus_hook_read_gr(s, s->gr_index, &val)) + break; + val = s->gr[s->gr_index]; +#ifdef DEBUG_VGA_REG + printf("vga: read GR%x = 0x%02x\n", s->gr_index, val); +#endif + break; + case 0x3b4: + case 0x3d4: + val = s->cr_index; + break; + case 0x3b5: + case 0x3d5: + if (cirrus_hook_read_cr(s, s->cr_index, &val)) + break; + val = s->cr[s->cr_index]; +#ifdef DEBUG_VGA_REG + printf("vga: read CR%x = 0x%02x\n", s->cr_index, val); +#endif + break; + case 0x3ba: + case 0x3da: + /* just toggle to fool polling */ + s->st01 ^= ST01_V_RETRACE | ST01_DISP_ENABLE; + val = s->st01; + s->ar_flip_flop = 0; + break; + default: + val = 0x00; + break; + } + } +#if defined(DEBUG_VGA) + printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val); +#endif + return val; +} + +static void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + CirrusVGAState *s = opaque; + int index; + + /* check port range access depending on color/monochrome mode */ + if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) + || (addr >= 0x3d0 && addr <= 0x3df + && !(s->msr & MSR_COLOR_EMULATION))) + return; + +#ifdef DEBUG_VGA + printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val); +#endif + + switch (addr) { + case 0x3c0: + if (s->ar_flip_flop == 0) { + val &= 0x3f; + s->ar_index = val; + } else { + index = s->ar_index & 0x1f; + switch (index) { + case 0x00 ... 0x0f: + s->ar[index] = val & 0x3f; + break; + case 0x10: + s->ar[index] = val & ~0x10; + break; + case 0x11: + s->ar[index] = val; + break; + case 0x12: + s->ar[index] = val & ~0xc0; + break; + case 0x13: + s->ar[index] = val & ~0xf0; + break; + case 0x14: + s->ar[index] = val & ~0xf0; + break; + default: + break; + } + } + s->ar_flip_flop ^= 1; + break; + case 0x3c2: + s->msr = val & ~0x10; + break; + case 0x3c4: + s->sr_index = val; + break; + case 0x3c5: + if (cirrus_hook_write_sr(s, s->sr_index, val)) + break; +#ifdef DEBUG_VGA_REG + printf("vga: write SR%x = 0x%02x\n", s->sr_index, val); +#endif + s->sr[s->sr_index] = val & sr_mask[s->sr_index]; + break; + case 0x3c6: + cirrus_write_hidden_dac(s, val); + break; + case 0x3c7: + s->dac_read_index = val; + s->dac_sub_index = 0; + s->dac_state = 3; + break; + case 0x3c8: + s->dac_write_index = val; + s->dac_sub_index = 0; + s->dac_state = 0; + break; + case 0x3c9: + if (cirrus_hook_write_palette(s, val)) + break; + s->dac_cache[s->dac_sub_index] = val; + if (++s->dac_sub_index == 3) { + memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3); + s->dac_sub_index = 0; + s->dac_write_index++; + } + break; + case 0x3ce: + s->gr_index = val; + break; + case 0x3cf: + if (cirrus_hook_write_gr(s, s->gr_index, val)) + break; +#ifdef DEBUG_VGA_REG + printf("vga: write GR%x = 0x%02x\n", s->gr_index, val); +#endif + s->gr[s->gr_index] = val & gr_mask[s->gr_index]; + break; + case 0x3b4: + case 0x3d4: + s->cr_index = val; + break; + case 0x3b5: + case 0x3d5: + if (cirrus_hook_write_cr(s, s->cr_index, val)) + break; +#ifdef DEBUG_VGA_REG + printf("vga: write CR%x = 0x%02x\n", s->cr_index, val); +#endif + /* handle CR0-7 protection */ + if ((s->cr[0x11] & 0x80) && s->cr_index <= 7) { + /* can always write bit 4 of CR7 */ + if (s->cr_index == 7) + s->cr[7] = (s->cr[7] & ~0x10) | (val & 0x10); + return; + } + switch (s->cr_index) { + case 0x01: /* horizontal display end */ + case 0x07: + case 0x09: + case 0x0c: + case 0x0d: + case 0x12: /* veritcal display end */ + s->cr[s->cr_index] = val; + break; + + default: + s->cr[s->cr_index] = val; + break; + } + break; + case 0x3ba: + case 0x3da: + s->fcr = val & 0x10; + break; + } +} + +/*************************************** + * + * memory-mapped I/O access + * + ***************************************/ + +static uint32_t cirrus_mmio_readb(void *opaque, target_phys_addr_t addr) +{ + CirrusVGAState *s = (CirrusVGAState *) opaque; + + addr &= CIRRUS_PNPMMIO_SIZE - 1; + + if (addr >= 0x100) { + return cirrus_mmio_blt_read(s, addr - 0x100); + } else { + return vga_ioport_read(s, addr + 0x3c0); + } +} + +static uint32_t cirrus_mmio_readw(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; +#ifdef TARGET_WORDS_BIGENDIAN + v = cirrus_mmio_readb(opaque, addr) << 8; + v |= cirrus_mmio_readb(opaque, addr + 1); +#else + v = cirrus_mmio_readb(opaque, addr); + v |= cirrus_mmio_readb(opaque, addr + 1) << 8; +#endif + return v; +} + +static uint32_t cirrus_mmio_readl(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; +#ifdef TARGET_WORDS_BIGENDIAN + v = cirrus_mmio_readb(opaque, addr) << 24; + v |= cirrus_mmio_readb(opaque, addr + 1) << 16; + v |= cirrus_mmio_readb(opaque, addr + 2) << 8; + v |= cirrus_mmio_readb(opaque, addr + 3); +#else + v = cirrus_mmio_readb(opaque, addr); + v |= cirrus_mmio_readb(opaque, addr + 1) << 8; + v |= cirrus_mmio_readb(opaque, addr + 2) << 16; + v |= cirrus_mmio_readb(opaque, addr + 3) << 24; +#endif + return v; +} + +static void cirrus_mmio_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + CirrusVGAState *s = (CirrusVGAState *) opaque; + + addr &= CIRRUS_PNPMMIO_SIZE - 1; + + if (addr >= 0x100) { + cirrus_mmio_blt_write(s, addr - 0x100, val); + } else { + vga_ioport_write(s, addr + 0x3c0, val); + } +} + +static void cirrus_mmio_writew(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ +#ifdef TARGET_WORDS_BIGENDIAN + cirrus_mmio_writeb(opaque, addr, (val >> 8) & 0xff); + cirrus_mmio_writeb(opaque, addr + 1, val & 0xff); +#else + cirrus_mmio_writeb(opaque, addr, val & 0xff); + cirrus_mmio_writeb(opaque, addr + 1, (val >> 8) & 0xff); +#endif +} + +static void cirrus_mmio_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ +#ifdef TARGET_WORDS_BIGENDIAN + cirrus_mmio_writeb(opaque, addr, (val >> 24) & 0xff); + cirrus_mmio_writeb(opaque, addr + 1, (val >> 16) & 0xff); + cirrus_mmio_writeb(opaque, addr + 2, (val >> 8) & 0xff); + cirrus_mmio_writeb(opaque, addr + 3, val & 0xff); +#else + cirrus_mmio_writeb(opaque, addr, val & 0xff); + cirrus_mmio_writeb(opaque, addr + 1, (val >> 8) & 0xff); + cirrus_mmio_writeb(opaque, addr + 2, (val >> 16) & 0xff); + cirrus_mmio_writeb(opaque, addr + 3, (val >> 24) & 0xff); +#endif +} + + +static CPUReadMemoryFunc *cirrus_mmio_read[3] = { + cirrus_mmio_readb, + cirrus_mmio_readw, + cirrus_mmio_readl, +}; + +static CPUWriteMemoryFunc *cirrus_mmio_write[3] = { + cirrus_mmio_writeb, + cirrus_mmio_writew, + cirrus_mmio_writel, +}; + +/* load/save state */ + +static void cirrus_vga_save(QEMUFile *f, void *opaque) +{ + CirrusVGAState *s = opaque; + + qemu_put_be32s(f, &s->latch); + qemu_put_8s(f, &s->sr_index); + qemu_put_buffer(f, s->sr, 256); + qemu_put_8s(f, &s->gr_index); + qemu_put_8s(f, &s->cirrus_shadow_gr0); + qemu_put_8s(f, &s->cirrus_shadow_gr1); + qemu_put_buffer(f, s->gr + 2, 254); + qemu_put_8s(f, &s->ar_index); + qemu_put_buffer(f, s->ar, 21); + qemu_put_be32s(f, &s->ar_flip_flop); + qemu_put_8s(f, &s->cr_index); + qemu_put_buffer(f, s->cr, 256); + qemu_put_8s(f, &s->msr); + qemu_put_8s(f, &s->fcr); + qemu_put_8s(f, &s->st00); + qemu_put_8s(f, &s->st01); + + qemu_put_8s(f, &s->dac_state); + qemu_put_8s(f, &s->dac_sub_index); + qemu_put_8s(f, &s->dac_read_index); + qemu_put_8s(f, &s->dac_write_index); + qemu_put_buffer(f, s->dac_cache, 3); + qemu_put_buffer(f, s->palette, 768); + + qemu_put_be32s(f, &s->bank_offset); + + qemu_put_8s(f, &s->cirrus_hidden_dac_lockindex); + qemu_put_8s(f, &s->cirrus_hidden_dac_data); + + qemu_put_be32s(f, &s->hw_cursor_x); + qemu_put_be32s(f, &s->hw_cursor_y); + /* XXX: we do not save the bitblt state - we assume we do not save + the state when the blitter is active */ +} + +static int cirrus_vga_load(QEMUFile *f, void *opaque, int version_id) +{ + CirrusVGAState *s = opaque; + + if (version_id != 1) + return -EINVAL; + + qemu_get_be32s(f, &s->latch); + qemu_get_8s(f, &s->sr_index); + qemu_get_buffer(f, s->sr, 256); + qemu_get_8s(f, &s->gr_index); + qemu_get_8s(f, &s->cirrus_shadow_gr0); + qemu_get_8s(f, &s->cirrus_shadow_gr1); + s->gr[0x00] = s->cirrus_shadow_gr0 & 0x0f; + s->gr[0x01] = s->cirrus_shadow_gr1 & 0x0f; + qemu_get_buffer(f, s->gr + 2, 254); + qemu_get_8s(f, &s->ar_index); + qemu_get_buffer(f, s->ar, 21); + qemu_get_be32s(f, &s->ar_flip_flop); + qemu_get_8s(f, &s->cr_index); + qemu_get_buffer(f, s->cr, 256); + qemu_get_8s(f, &s->msr); + qemu_get_8s(f, &s->fcr); + qemu_get_8s(f, &s->st00); + qemu_get_8s(f, &s->st01); + + qemu_get_8s(f, &s->dac_state); + qemu_get_8s(f, &s->dac_sub_index); + qemu_get_8s(f, &s->dac_read_index); + qemu_get_8s(f, &s->dac_write_index); + qemu_get_buffer(f, s->dac_cache, 3); + qemu_get_buffer(f, s->palette, 768); + + qemu_get_be32s(f, &s->bank_offset); + + qemu_get_8s(f, &s->cirrus_hidden_dac_lockindex); + qemu_get_8s(f, &s->cirrus_hidden_dac_data); + + qemu_get_be32s(f, &s->hw_cursor_x); + qemu_get_be32s(f, &s->hw_cursor_y); + + /* force refresh */ + s->graphic_mode = -1; + cirrus_update_bank_ptr(s, 0); + cirrus_update_bank_ptr(s, 1); + return 0; +} + +/*************************************** + * + * initialize + * + ***************************************/ + +static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci) +{ + int vga_io_memory, i; + static int inited; + + if (!inited) { + inited = 1; + for(i = 0;i < 256; i++) + rop_to_index[i] = CIRRUS_ROP_NOP_INDEX; /* nop rop */ + rop_to_index[CIRRUS_ROP_0] = 0; + rop_to_index[CIRRUS_ROP_SRC_AND_DST] = 1; + rop_to_index[CIRRUS_ROP_NOP] = 2; + rop_to_index[CIRRUS_ROP_SRC_AND_NOTDST] = 3; + rop_to_index[CIRRUS_ROP_NOTDST] = 4; + rop_to_index[CIRRUS_ROP_SRC] = 5; + rop_to_index[CIRRUS_ROP_1] = 6; + rop_to_index[CIRRUS_ROP_NOTSRC_AND_DST] = 7; + rop_to_index[CIRRUS_ROP_SRC_XOR_DST] = 8; + rop_to_index[CIRRUS_ROP_SRC_OR_DST] = 9; + rop_to_index[CIRRUS_ROP_NOTSRC_OR_NOTDST] = 10; + rop_to_index[CIRRUS_ROP_SRC_NOTXOR_DST] = 11; + rop_to_index[CIRRUS_ROP_SRC_OR_NOTDST] = 12; + rop_to_index[CIRRUS_ROP_NOTSRC] = 13; + rop_to_index[CIRRUS_ROP_NOTSRC_OR_DST] = 14; + rop_to_index[CIRRUS_ROP_NOTSRC_AND_NOTDST] = 15; + } + + register_ioport_write(0x3c0, 16, 1, vga_ioport_write, s); + + register_ioport_write(0x3b4, 2, 1, vga_ioport_write, s); + register_ioport_write(0x3d4, 2, 1, vga_ioport_write, s); + register_ioport_write(0x3ba, 1, 1, vga_ioport_write, s); + register_ioport_write(0x3da, 1, 1, vga_ioport_write, s); + + register_ioport_read(0x3c0, 16, 1, vga_ioport_read, s); + + register_ioport_read(0x3b4, 2, 1, vga_ioport_read, s); + register_ioport_read(0x3d4, 2, 1, vga_ioport_read, s); + register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s); + register_ioport_read(0x3da, 1, 1, vga_ioport_read, s); + + vga_io_memory = cpu_register_io_memory(0, cirrus_vga_mem_read, + cirrus_vga_mem_write, s); + cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000, + vga_io_memory); + + s->sr[0x06] = 0x0f; + if (device_id == CIRRUS_ID_CLGD5446) { + /* 4MB 64 bit memory config, always PCI */ + s->sr[0x1F] = 0x2d; // MemClock + s->gr[0x18] = 0x0f; // fastest memory configuration +#if 1 + s->sr[0x0f] = 0x98; + s->sr[0x17] = 0x20; + s->sr[0x15] = 0x04; /* memory size, 3=2MB, 4=4MB */ + s->real_vram_size = 4096 * 1024; +#else + s->sr[0x0f] = 0x18; + s->sr[0x17] = 0x20; + s->sr[0x15] = 0x03; /* memory size, 3=2MB, 4=4MB */ + s->real_vram_size = 2048 * 1024; +#endif + } else { + s->sr[0x1F] = 0x22; // MemClock + s->sr[0x0F] = CIRRUS_MEMSIZE_2M; + if (is_pci) + s->sr[0x17] = CIRRUS_BUSTYPE_PCI; + else + s->sr[0x17] = CIRRUS_BUSTYPE_ISA; + s->real_vram_size = 2048 * 1024; + s->sr[0x15] = 0x03; /* memory size, 3=2MB, 4=4MB */ + } + s->cr[0x27] = device_id; + + /* Win2K seems to assume that the pattern buffer is at 0xff + initially ! */ + memset(s->vram_ptr, 0xff, s->real_vram_size); + + s->cirrus_hidden_dac_lockindex = 5; + s->cirrus_hidden_dac_data = 0; + + /* I/O handler for LFB */ + s->cirrus_linear_io_addr = + cpu_register_io_memory(0, cirrus_linear_read, cirrus_linear_write, + s); + s->cirrus_linear_write = cpu_get_io_memory_write(s->cirrus_linear_io_addr); + + /* I/O handler for LFB */ + s->cirrus_linear_bitblt_io_addr = + cpu_register_io_memory(0, cirrus_linear_bitblt_read, cirrus_linear_bitblt_write, + s); + + /* I/O handler for memory-mapped I/O */ + s->cirrus_mmio_io_addr = + cpu_register_io_memory(0, cirrus_mmio_read, cirrus_mmio_write, s); + + /* XXX: s->vram_size must be a power of two */ + s->cirrus_addr_mask = s->real_vram_size - 1; + s->linear_mmio_mask = s->real_vram_size - 256; + + s->get_bpp = cirrus_get_bpp; + s->get_offsets = cirrus_get_offsets; + s->get_resolution = cirrus_get_resolution; + s->cursor_invalidate = cirrus_cursor_invalidate; + s->cursor_draw_line = cirrus_cursor_draw_line; + + register_savevm("cirrus_vga", 0, 1, cirrus_vga_save, cirrus_vga_load, s); +} + +/*************************************** + * + * ISA bus support + * + ***************************************/ + +void isa_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base, + unsigned long vga_ram_offset, int vga_ram_size) +{ + CirrusVGAState *s; + + s = qemu_mallocz(sizeof(CirrusVGAState)); + + vga_common_init((VGAState *)s, + ds, vga_ram_base, vga_ram_offset, vga_ram_size); + cirrus_init_common(s, CIRRUS_ID_CLGD5430, 0); + /* XXX ISA-LFB support */ +} + +/*************************************** + * + * PCI bus support + * + ***************************************/ + +static void cirrus_pci_lfb_map(PCIDevice *d, int region_num, + uint32_t addr, uint32_t size, int type) +{ + CirrusVGAState *s = &((PCICirrusVGAState *)d)->cirrus_vga; + + /* XXX: add byte swapping apertures */ + cpu_register_physical_memory(addr, s->vram_size, + s->cirrus_linear_io_addr); + s->cirrus_lfb_addr = addr; + s->cirrus_lfb_end = addr + VGA_RAM_SIZE; + + if (s->map_addr && (s->cirrus_lfb_addr != s->map_addr) && + (s->cirrus_lfb_end != s->map_end)) + fprintf(logfile, "cirrus vga map change while on lfb mode\n"); + + cpu_register_physical_memory(addr + 0x1000000, 0x400000, + s->cirrus_linear_bitblt_io_addr); +} + +static void cirrus_pci_mmio_map(PCIDevice *d, int region_num, + uint32_t addr, uint32_t size, int type) +{ + CirrusVGAState *s = &((PCICirrusVGAState *)d)->cirrus_vga; + + cpu_register_physical_memory(addr, CIRRUS_PNPMMIO_SIZE, + s->cirrus_mmio_io_addr); +} + +void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, + unsigned long vga_ram_offset, int vga_ram_size) +{ + PCICirrusVGAState *d; + uint8_t *pci_conf; + CirrusVGAState *s; + int device_id; + + device_id = CIRRUS_ID_CLGD5446; + + /* setup PCI configuration registers */ + d = (PCICirrusVGAState *)pci_register_device(bus, "Cirrus VGA", + sizeof(PCICirrusVGAState), + -1, NULL, NULL); + pci_conf = d->dev.config; + pci_conf[0x00] = (uint8_t) (PCI_VENDOR_CIRRUS & 0xff); + pci_conf[0x01] = (uint8_t) (PCI_VENDOR_CIRRUS >> 8); + pci_conf[0x02] = (uint8_t) (device_id & 0xff); + pci_conf[0x03] = (uint8_t) (device_id >> 8); + pci_conf[0x04] = PCI_COMMAND_IOACCESS | PCI_COMMAND_MEMACCESS; + pci_conf[0x0a] = PCI_CLASS_SUB_VGA; + pci_conf[0x0b] = PCI_CLASS_BASE_DISPLAY; + pci_conf[0x0e] = PCI_CLASS_HEADERTYPE_00h; + + /* setup VGA */ + s = &d->cirrus_vga; + vga_common_init((VGAState *)s, + ds, vga_ram_base, vga_ram_offset, vga_ram_size); + cirrus_init_common(s, device_id, 1); + + /* setup memory space */ + /* memory #0 LFB */ + /* memory #1 memory-mapped I/O */ + /* XXX: s->vram_size must be a power of two */ + pci_register_io_region((PCIDevice *)d, 0, 0x2000000, + PCI_ADDRESS_SPACE_MEM_PREFETCH, cirrus_pci_lfb_map); + if (device_id == CIRRUS_ID_CLGD5446) { + pci_register_io_region((PCIDevice *)d, 1, CIRRUS_PNPMMIO_SIZE, + PCI_ADDRESS_SPACE_MEM, cirrus_pci_mmio_map); + } + /* XXX: ROM BIOS */ +} diff --git a/tools/ioemu/hw/cirrus_vga_rop.h b/tools/ioemu/hw/cirrus_vga_rop.h new file mode 100644 index 0000000000..c54f1258b3 --- /dev/null +++ b/tools/ioemu/hw/cirrus_vga_rop.h @@ -0,0 +1,78 @@ +/* + * QEMU Cirrus CLGD 54xx VGA Emulator. + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +static void +glue(cirrus_bitblt_rop_fwd_, ROP_NAME)(CirrusVGAState *s, + uint8_t *dst,const uint8_t *src, + int dstpitch,int srcpitch, + int bltwidth,int bltheight) +{ + int x,y; + dstpitch -= bltwidth; + srcpitch -= bltwidth; + for (y = 0; y < bltheight; y++) { + for (x = 0; x < bltwidth; x++) { + ROP_OP(*dst, *src); + dst++; + src++; + } + dst += dstpitch; + src += srcpitch; + } +} + +static void +glue(cirrus_bitblt_rop_bkwd_, ROP_NAME)(CirrusVGAState *s, + uint8_t *dst,const uint8_t *src, + int dstpitch,int srcpitch, + int bltwidth,int bltheight) +{ + int x,y; + dstpitch += bltwidth; + srcpitch += bltwidth; + for (y = 0; y < bltheight; y++) { + for (x = 0; x < bltwidth; x++) { + ROP_OP(*dst, *src); + dst--; + src--; + } + dst += dstpitch; + src += srcpitch; + } +} + +#define DEPTH 8 +#include "cirrus_vga_rop2.h" + +#define DEPTH 16 +#include "cirrus_vga_rop2.h" + +#define DEPTH 24 +#include "cirrus_vga_rop2.h" + +#define DEPTH 32 +#include "cirrus_vga_rop2.h" + +#undef ROP_NAME +#undef ROP_OP diff --git a/tools/ioemu/hw/cirrus_vga_rop2.h b/tools/ioemu/hw/cirrus_vga_rop2.h new file mode 100644 index 0000000000..da11d0f5e5 --- /dev/null +++ b/tools/ioemu/hw/cirrus_vga_rop2.h @@ -0,0 +1,281 @@ +/* + * QEMU Cirrus CLGD 54xx VGA Emulator. + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if DEPTH == 8 +#define PUTPIXEL() ROP_OP(d[0], col) +#elif DEPTH == 16 +#define PUTPIXEL() ROP_OP(((uint16_t *)d)[0], col); +#elif DEPTH == 24 +#define PUTPIXEL() ROP_OP(d[0], col); \ + ROP_OP(d[1], (col >> 8)); \ + ROP_OP(d[2], (col >> 16)) +#elif DEPTH == 32 +#define PUTPIXEL() ROP_OP(((uint32_t *)d)[0], col) +#else +#error unsupported DEPTH +#endif + +static void +glue(glue(glue(cirrus_patternfill_, ROP_NAME), _),DEPTH) + (CirrusVGAState * s, uint8_t * dst, + const uint8_t * src, + int dstpitch, int srcpitch, + int bltwidth, int bltheight) +{ + uint8_t *d; + int x, y, pattern_y, pattern_pitch, pattern_x; + unsigned int col; + const uint8_t *src1; +#if DEPTH == 24 + int skipleft = s->gr[0x2f] & 0x1f; +#else + int skipleft = (s->gr[0x2f] & 0x07) * (DEPTH / 8); +#endif + +#if DEPTH == 8 + pattern_pitch = 8; +#elif DEPTH == 16 + pattern_pitch = 16; +#else + pattern_pitch = 32; +#endif + pattern_y = s->cirrus_blt_srcaddr & 7; + for(y = 0; y < bltheight; y++) { + pattern_x = skipleft; + d = dst + skipleft; + src1 = src + pattern_y * pattern_pitch; + for (x = skipleft; x < bltwidth; x += (DEPTH / 8)) { +#if DEPTH == 8 + col = src1[pattern_x]; + pattern_x = (pattern_x + 1) & 7; +#elif DEPTH == 16 + col = ((uint16_t *)(src1 + pattern_x))[0]; + pattern_x = (pattern_x + 2) & 15; +#elif DEPTH == 24 + { + const uint8_t *src2 = src1 + pattern_x * 3; + col = src2[0] | (src2[1] << 8) | (src2[2] << 16); + pattern_x = (pattern_x + 1) & 7; + } +#else + col = ((uint32_t *)(src1 + pattern_x))[0]; + pattern_x = (pattern_x + 4) & 31; +#endif + PUTPIXEL(); + d += (DEPTH / 8); + } + pattern_y = (pattern_y + 1) & 7; + dst += dstpitch; + } +} + +/* NOTE: srcpitch is ignored */ +static void +glue(glue(glue(cirrus_colorexpand_transp_, ROP_NAME), _),DEPTH) + (CirrusVGAState * s, uint8_t * dst, + const uint8_t * src, + int dstpitch, int srcpitch, + int bltwidth, int bltheight) +{ + uint8_t *d; + int x, y; + unsigned bits, bits_xor; + unsigned int col; + unsigned bitmask; + unsigned index; +#if DEPTH == 24 + int dstskipleft = s->gr[0x2f] & 0x1f; + int srcskipleft = dstskipleft / 3; +#else + int srcskipleft = s->gr[0x2f] & 0x07; + int dstskipleft = srcskipleft * (DEPTH / 8); +#endif + + if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) { + bits_xor = 0xff; + col = s->cirrus_blt_bgcol; + } else { + bits_xor = 0x00; + col = s->cirrus_blt_fgcol; + } + + for(y = 0; y < bltheight; y++) { + bitmask = 0x80 >> srcskipleft; + bits = *src++ ^ bits_xor; + d = dst + dstskipleft; + for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) { + if ((bitmask & 0xff) == 0) { + bitmask = 0x80; + bits = *src++ ^ bits_xor; + } + index = (bits & bitmask); + if (index) { + PUTPIXEL(); + } + d += (DEPTH / 8); + bitmask >>= 1; + } + dst += dstpitch; + } +} + +static void +glue(glue(glue(cirrus_colorexpand_, ROP_NAME), _),DEPTH) + (CirrusVGAState * s, uint8_t * dst, + const uint8_t * src, + int dstpitch, int srcpitch, + int bltwidth, int bltheight) +{ + uint32_t colors[2]; + uint8_t *d; + int x, y; + unsigned bits; + unsigned int col; + unsigned bitmask; + int srcskipleft = s->gr[0x2f] & 0x07; + int dstskipleft = srcskipleft * (DEPTH / 8); + + colors[0] = s->cirrus_blt_bgcol; + colors[1] = s->cirrus_blt_fgcol; + for(y = 0; y < bltheight; y++) { + bitmask = 0x80 >> srcskipleft; + bits = *src++; + d = dst + dstskipleft; + for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) { + if ((bitmask & 0xff) == 0) { + bitmask = 0x80; + bits = *src++; + } + col = colors[!!(bits & bitmask)]; + PUTPIXEL(); + d += (DEPTH / 8); + bitmask >>= 1; + } + dst += dstpitch; + } +} + +static void +glue(glue(glue(cirrus_colorexpand_pattern_transp_, ROP_NAME), _),DEPTH) + (CirrusVGAState * s, uint8_t * dst, + const uint8_t * src, + int dstpitch, int srcpitch, + int bltwidth, int bltheight) +{ + uint8_t *d; + int x, y, bitpos, pattern_y; + unsigned int bits, bits_xor; + unsigned int col; +#if DEPTH == 24 + int dstskipleft = s->gr[0x2f] & 0x1f; + int srcskipleft = dstskipleft / 3; +#else + int srcskipleft = s->gr[0x2f] & 0x07; + int dstskipleft = srcskipleft * (DEPTH / 8); +#endif + + if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) { + bits_xor = 0xff; + col = s->cirrus_blt_bgcol; + } else { + bits_xor = 0x00; + col = s->cirrus_blt_fgcol; + } + pattern_y = s->cirrus_blt_srcaddr & 7; + + for(y = 0; y < bltheight; y++) { + bits = src[pattern_y] ^ bits_xor; + bitpos = 7 - srcskipleft; + d = dst + dstskipleft; + for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) { + if ((bits >> bitpos) & 1) { + PUTPIXEL(); + } + d += (DEPTH / 8); + bitpos = (bitpos - 1) & 7; + } + pattern_y = (pattern_y + 1) & 7; + dst += dstpitch; + } +} + +static void +glue(glue(glue(cirrus_colorexpand_pattern_, ROP_NAME), _),DEPTH) + (CirrusVGAState * s, uint8_t * dst, + const uint8_t * src, + int dstpitch, int srcpitch, + int bltwidth, int bltheight) +{ + uint32_t colors[2]; + uint8_t *d; + int x, y, bitpos, pattern_y; + unsigned int bits; + unsigned int col; + int srcskipleft = s->gr[0x2f] & 0x07; + int dstskipleft = srcskipleft * (DEPTH / 8); + + colors[0] = s->cirrus_blt_bgcol; + colors[1] = s->cirrus_blt_fgcol; + pattern_y = s->cirrus_blt_srcaddr & 7; + + for(y = 0; y < bltheight; y++) { + bits = src[pattern_y]; + bitpos = 7 - srcskipleft; + d = dst + dstskipleft; + for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) { + col = colors[(bits >> bitpos) & 1]; + PUTPIXEL(); + d += (DEPTH / 8); + bitpos = (bitpos - 1) & 7; + } + pattern_y = (pattern_y + 1) & 7; + dst += dstpitch; + } +} + +static void +glue(glue(glue(cirrus_fill_, ROP_NAME), _),DEPTH) + (CirrusVGAState *s, + uint8_t *dst, int dst_pitch, + int width, int height) +{ + uint8_t *d, *d1; + uint32_t col; + int x, y; + + col = s->cirrus_blt_fgcol; + + d1 = dst; + for(y = 0; y < height; y++) { + d = d1; + for(x = 0; x < width; x += (DEPTH / 8)) { + PUTPIXEL(); + d += (DEPTH / 8); + } + d1 += dst_pitch; + } +} + +#undef DEPTH +#undef PUTPIXEL diff --git a/tools/ioemu/hw/cuda.c b/tools/ioemu/hw/cuda.c new file mode 100644 index 0000000000..dec5ffb310 --- /dev/null +++ b/tools/ioemu/hw/cuda.c @@ -0,0 +1,656 @@ +/* + * QEMU CUDA support + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* XXX: implement all timer modes */ + +//#define DEBUG_CUDA +//#define DEBUG_CUDA_PACKET + +/* Bits in B data register: all active low */ +#define TREQ 0x08 /* Transfer request (input) */ +#define TACK 0x10 /* Transfer acknowledge (output) */ +#define TIP 0x20 /* Transfer in progress (output) */ + +/* Bits in ACR */ +#define SR_CTRL 0x1c /* Shift register control bits */ +#define SR_EXT 0x0c /* Shift on external clock */ +#define SR_OUT 0x10 /* Shift out if 1 */ + +/* Bits in IFR and IER */ +#define IER_SET 0x80 /* set bits in IER */ +#define IER_CLR 0 /* clear bits in IER */ +#define SR_INT 0x04 /* Shift register full/empty */ +#define T1_INT 0x40 /* Timer 1 interrupt */ +#define T2_INT 0x20 /* Timer 2 interrupt */ + +/* Bits in ACR */ +#define T1MODE 0xc0 /* Timer 1 mode */ +#define T1MODE_CONT 0x40 /* continuous interrupts */ + +/* commands (1st byte) */ +#define ADB_PACKET 0 +#define CUDA_PACKET 1 +#define ERROR_PACKET 2 +#define TIMER_PACKET 3 +#define POWER_PACKET 4 +#define MACIIC_PACKET 5 +#define PMU_PACKET 6 + + +/* CUDA commands (2nd byte) */ +#define CUDA_WARM_START 0x0 +#define CUDA_AUTOPOLL 0x1 +#define CUDA_GET_6805_ADDR 0x2 +#define CUDA_GET_TIME 0x3 +#define CUDA_GET_PRAM 0x7 +#define CUDA_SET_6805_ADDR 0x8 +#define CUDA_SET_TIME 0x9 +#define CUDA_POWERDOWN 0xa +#define CUDA_POWERUP_TIME 0xb +#define CUDA_SET_PRAM 0xc +#define CUDA_MS_RESET 0xd +#define CUDA_SEND_DFAC 0xe +#define CUDA_BATTERY_SWAP_SENSE 0x10 +#define CUDA_RESET_SYSTEM 0x11 +#define CUDA_SET_IPL 0x12 +#define CUDA_FILE_SERVER_FLAG 0x13 +#define CUDA_SET_AUTO_RATE 0x14 +#define CUDA_GET_AUTO_RATE 0x16 +#define CUDA_SET_DEVICE_LIST 0x19 +#define CUDA_GET_DEVICE_LIST 0x1a +#define CUDA_SET_ONE_SECOND_MODE 0x1b +#define CUDA_SET_POWER_MESSAGES 0x21 +#define CUDA_GET_SET_IIC 0x22 +#define CUDA_WAKEUP 0x23 +#define CUDA_TIMER_TICKLE 0x24 +#define CUDA_COMBINED_FORMAT_IIC 0x25 + +#define CUDA_TIMER_FREQ (4700000 / 6) +#define CUDA_ADB_POLL_FREQ 50 + +/* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */ +#define RTC_OFFSET 2082844800 + +typedef struct CUDATimer { + int index; + uint16_t latch; + uint16_t counter_value; /* counter value at load time */ + int64_t load_time; + int64_t next_irq_time; + QEMUTimer *timer; +} CUDATimer; + +typedef struct CUDAState { + /* cuda registers */ + uint8_t b; /* B-side data */ + uint8_t a; /* A-side data */ + uint8_t dirb; /* B-side direction (1=output) */ + uint8_t dira; /* A-side direction (1=output) */ + uint8_t sr; /* Shift register */ + uint8_t acr; /* Auxiliary control register */ + uint8_t pcr; /* Peripheral control register */ + uint8_t ifr; /* Interrupt flag register */ + uint8_t ier; /* Interrupt enable register */ + uint8_t anh; /* A-side data, no handshake */ + + CUDATimer timers[2]; + + uint8_t last_b; /* last value of B register */ + uint8_t last_acr; /* last value of B register */ + + int data_in_size; + int data_in_index; + int data_out_index; + + SetIRQFunc *set_irq; + int irq; + void *irq_opaque; + uint8_t autopoll; + uint8_t data_in[128]; + uint8_t data_out[16]; + QEMUTimer *adb_poll_timer; +} CUDAState; + +static CUDAState cuda_state; +ADBBusState adb_bus; + +static void cuda_update(CUDAState *s); +static void cuda_receive_packet_from_host(CUDAState *s, + const uint8_t *data, int len); +static void cuda_timer_update(CUDAState *s, CUDATimer *ti, + int64_t current_time); + +static void cuda_update_irq(CUDAState *s) +{ + if (s->ifr & s->ier & (SR_INT | T1_INT)) { + s->set_irq(s->irq_opaque, s->irq, 1); + } else { + s->set_irq(s->irq_opaque, s->irq, 0); + } +} + +static unsigned int get_counter(CUDATimer *s) +{ + int64_t d; + unsigned int counter; + + d = muldiv64(qemu_get_clock(vm_clock) - s->load_time, + CUDA_TIMER_FREQ, ticks_per_sec); + if (s->index == 0) { + /* the timer goes down from latch to -1 (period of latch + 2) */ + if (d <= (s->counter_value + 1)) { + counter = (s->counter_value - d) & 0xffff; + } else { + counter = (d - (s->counter_value + 1)) % (s->latch + 2); + counter = (s->latch - counter) & 0xffff; + } + } else { + counter = (s->counter_value - d) & 0xffff; + } + return counter; +} + +static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val) +{ +#ifdef DEBUG_CUDA + printf("cuda: T%d.counter=%d\n", + 1 + (ti->timer == NULL), val); +#endif + ti->load_time = qemu_get_clock(vm_clock); + ti->counter_value = val; + cuda_timer_update(s, ti, ti->load_time); +} + +static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time) +{ + int64_t d, next_time; + unsigned int counter; + + /* current counter value */ + d = muldiv64(current_time - s->load_time, + CUDA_TIMER_FREQ, ticks_per_sec); + /* the timer goes down from latch to -1 (period of latch + 2) */ + if (d <= (s->counter_value + 1)) { + counter = (s->counter_value - d) & 0xffff; + } else { + counter = (d - (s->counter_value + 1)) % (s->latch + 2); + counter = (s->latch - counter) & 0xffff; + } + + /* Note: we consider the irq is raised on 0 */ + if (counter == 0xffff) { + next_time = d + s->latch + 1; + } else if (counter == 0) { + next_time = d + s->latch + 2; + } else { + next_time = d + counter; + } +#if 0 +#ifdef DEBUG_CUDA + printf("latch=%d counter=%lld delta_next=%lld\n", + s->latch, d, next_time - d); +#endif +#endif + next_time = muldiv64(next_time, ticks_per_sec, CUDA_TIMER_FREQ) + + s->load_time; + if (next_time <= current_time) + next_time = current_time + 1; + return next_time; +} + +static void cuda_timer_update(CUDAState *s, CUDATimer *ti, + int64_t current_time) +{ + if (!ti->timer) + return; + if ((s->acr & T1MODE) != T1MODE_CONT) { + qemu_del_timer(ti->timer); + } else { + ti->next_irq_time = get_next_irq_time(ti, current_time); + qemu_mod_timer(ti->timer, ti->next_irq_time); + } +} + +static void cuda_timer1(void *opaque) +{ + CUDAState *s = opaque; + CUDATimer *ti = &s->timers[0]; + + cuda_timer_update(s, ti, ti->next_irq_time); + s->ifr |= T1_INT; + cuda_update_irq(s); +} + +static uint32_t cuda_readb(void *opaque, target_phys_addr_t addr) +{ + CUDAState *s = opaque; + uint32_t val; + + addr = (addr >> 9) & 0xf; + switch(addr) { + case 0: + val = s->b; + break; + case 1: + val = s->a; + break; + case 2: + val = s->dirb; + break; + case 3: + val = s->dira; + break; + case 4: + val = get_counter(&s->timers[0]) & 0xff; + s->ifr &= ~T1_INT; + cuda_update_irq(s); + break; + case 5: + val = get_counter(&s->timers[0]) >> 8; + cuda_update_irq(s); + break; + case 6: + val = s->timers[0].latch & 0xff; + break; + case 7: + /* XXX: check this */ + val = (s->timers[0].latch >> 8) & 0xff; + break; + case 8: + val = get_counter(&s->timers[1]) & 0xff; + s->ifr &= ~T2_INT; + break; + case 9: + val = get_counter(&s->timers[1]) >> 8; + break; + case 10: + val = s->sr; + s->ifr &= ~SR_INT; + cuda_update_irq(s); + break; + case 11: + val = s->acr; + break; + case 12: + val = s->pcr; + break; + case 13: + val = s->ifr; + if (s->ifr & s->ier) + val |= 0x80; + break; + case 14: + val = s->ier | 0x80; + break; + default: + case 15: + val = s->anh; + break; + } +#ifdef DEBUG_CUDA + if (addr != 13 || val != 0) + printf("cuda: read: reg=0x%x val=%02x\n", addr, val); +#endif + return val; +} + +static void cuda_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + CUDAState *s = opaque; + + addr = (addr >> 9) & 0xf; +#ifdef DEBUG_CUDA + printf("cuda: write: reg=0x%x val=%02x\n", addr, val); +#endif + + switch(addr) { + case 0: + s->b = val; + cuda_update(s); + break; + case 1: + s->a = val; + break; + case 2: + s->dirb = val; + break; + case 3: + s->dira = val; + break; + case 4: + s->timers[0].latch = (s->timers[0].latch & 0xff00) | val; + cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock)); + break; + case 5: + s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8); + s->ifr &= ~T1_INT; + set_counter(s, &s->timers[0], s->timers[0].latch); + break; + case 6: + s->timers[0].latch = (s->timers[0].latch & 0xff00) | val; + cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock)); + break; + case 7: + s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8); + s->ifr &= ~T1_INT; + cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock)); + break; + case 8: + s->timers[1].latch = val; + set_counter(s, &s->timers[1], val); + break; + case 9: + set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch); + break; + case 10: + s->sr = val; + break; + case 11: + s->acr = val; + cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock)); + cuda_update(s); + break; + case 12: + s->pcr = val; + break; + case 13: + /* reset bits */ + s->ifr &= ~val; + cuda_update_irq(s); + break; + case 14: + if (val & IER_SET) { + /* set bits */ + s->ier |= val & 0x7f; + } else { + /* reset bits */ + s->ier &= ~val; + } + cuda_update_irq(s); + break; + default: + case 15: + s->anh = val; + break; + } +} + +/* NOTE: TIP and TREQ are negated */ +static void cuda_update(CUDAState *s) +{ + int packet_received, len; + + packet_received = 0; + if (!(s->b & TIP)) { + /* transfer requested from host */ + + if (s->acr & SR_OUT) { + /* data output */ + if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { + if (s->data_out_index < sizeof(s->data_out)) { +#ifdef DEBUG_CUDA + printf("cuda: send: %02x\n", s->sr); +#endif + s->data_out[s->data_out_index++] = s->sr; + s->ifr |= SR_INT; + cuda_update_irq(s); + } + } + } else { + if (s->data_in_index < s->data_in_size) { + /* data input */ + if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { + s->sr = s->data_in[s->data_in_index++]; +#ifdef DEBUG_CUDA + printf("cuda: recv: %02x\n", s->sr); +#endif + /* indicate end of transfer */ + if (s->data_in_index >= s->data_in_size) { + s->b = (s->b | TREQ); + } + s->ifr |= SR_INT; + cuda_update_irq(s); + } + } + } + } else { + /* no transfer requested: handle sync case */ + if ((s->last_b & TIP) && (s->b & TACK) != (s->last_b & TACK)) { + /* update TREQ state each time TACK change state */ + if (s->b & TACK) + s->b = (s->b | TREQ); + else + s->b = (s->b & ~TREQ); + s->ifr |= SR_INT; + cuda_update_irq(s); + } else { + if (!(s->last_b & TIP)) { + /* handle end of host to cuda transfert */ + packet_received = (s->data_out_index > 0); + /* always an IRQ at the end of transfert */ + s->ifr |= SR_INT; + cuda_update_irq(s); + } + /* signal if there is data to read */ + if (s->data_in_index < s->data_in_size) { + s->b = (s->b & ~TREQ); + } + } + } + + s->last_acr = s->acr; + s->last_b = s->b; + + /* NOTE: cuda_receive_packet_from_host() can call cuda_update() + recursively */ + if (packet_received) { + len = s->data_out_index; + s->data_out_index = 0; + cuda_receive_packet_from_host(s, s->data_out, len); + } +} + +static void cuda_send_packet_to_host(CUDAState *s, + const uint8_t *data, int len) +{ +#ifdef DEBUG_CUDA_PACKET + { + int i; + printf("cuda_send_packet_to_host:\n"); + for(i = 0; i < len; i++) + printf(" %02x", data[i]); + printf("\n"); + } +#endif + memcpy(s->data_in, data, len); + s->data_in_size = len; + s->data_in_index = 0; + cuda_update(s); + s->ifr |= SR_INT; + cuda_update_irq(s); +} + +static void cuda_adb_poll(void *opaque) +{ + CUDAState *s = opaque; + uint8_t obuf[ADB_MAX_OUT_LEN + 2]; + int olen; + + olen = adb_poll(&adb_bus, obuf + 2); + if (olen > 0) { + obuf[0] = ADB_PACKET; + obuf[1] = 0x40; /* polled data */ + cuda_send_packet_to_host(s, obuf, olen + 2); + } + qemu_mod_timer(s->adb_poll_timer, + qemu_get_clock(vm_clock) + + (ticks_per_sec / CUDA_ADB_POLL_FREQ)); +} + +static void cuda_receive_packet(CUDAState *s, + const uint8_t *data, int len) +{ + uint8_t obuf[16]; + int ti, autopoll; + + switch(data[0]) { + case CUDA_AUTOPOLL: + autopoll = (data[1] != 0); + if (autopoll != s->autopoll) { + s->autopoll = autopoll; + if (autopoll) { + qemu_mod_timer(s->adb_poll_timer, + qemu_get_clock(vm_clock) + + (ticks_per_sec / CUDA_ADB_POLL_FREQ)); + } else { + qemu_del_timer(s->adb_poll_timer); + } + } + obuf[0] = CUDA_PACKET; + obuf[1] = data[1]; + cuda_send_packet_to_host(s, obuf, 2); + break; + case CUDA_GET_TIME: + case CUDA_SET_TIME: + /* XXX: add time support ? */ + ti = time(NULL) + RTC_OFFSET; + obuf[0] = CUDA_PACKET; + obuf[1] = 0; + obuf[2] = 0; + obuf[3] = ti >> 24; + obuf[4] = ti >> 16; + obuf[5] = ti >> 8; + obuf[6] = ti; + cuda_send_packet_to_host(s, obuf, 7); + break; + case CUDA_FILE_SERVER_FLAG: + case CUDA_SET_DEVICE_LIST: + case CUDA_SET_AUTO_RATE: + case CUDA_SET_POWER_MESSAGES: + obuf[0] = CUDA_PACKET; + obuf[1] = 0; + cuda_send_packet_to_host(s, obuf, 2); + break; + case CUDA_POWERDOWN: + obuf[0] = CUDA_PACKET; + obuf[1] = 0; + cuda_send_packet_to_host(s, obuf, 2); + qemu_system_shutdown_request(); + break; + default: + break; + } +} + +static void cuda_receive_packet_from_host(CUDAState *s, + const uint8_t *data, int len) +{ +#ifdef DEBUG_CUDA_PACKET + { + int i; + printf("cuda_receive_packet_from_host:\n"); + for(i = 0; i < len; i++) + printf(" %02x", data[i]); + printf("\n"); + } +#endif + switch(data[0]) { + case ADB_PACKET: + { + uint8_t obuf[ADB_MAX_OUT_LEN + 2]; + int olen; + olen = adb_request(&adb_bus, obuf + 2, data + 1, len - 1); + if (olen > 0) { + obuf[0] = ADB_PACKET; + obuf[1] = 0x00; + } else { + /* error */ + obuf[0] = ADB_PACKET; + obuf[1] = -olen; + olen = 0; + } + cuda_send_packet_to_host(s, obuf, olen + 2); + } + break; + case CUDA_PACKET: + cuda_receive_packet(s, data + 1, len - 1); + break; + } +} + +static void cuda_writew (void *opaque, target_phys_addr_t addr, uint32_t value) +{ +} + +static void cuda_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ +} + +static uint32_t cuda_readw (void *opaque, target_phys_addr_t addr) +{ + return 0; +} + +static uint32_t cuda_readl (void *opaque, target_phys_addr_t addr) +{ + return 0; +} + +static CPUWriteMemoryFunc *cuda_write[] = { + &cuda_writeb, + &cuda_writew, + &cuda_writel, +}; + +static CPUReadMemoryFunc *cuda_read[] = { + &cuda_readb, + &cuda_readw, + &cuda_readl, +}; + +int cuda_init(SetIRQFunc *set_irq, void *irq_opaque, int irq) +{ + CUDAState *s = &cuda_state; + int cuda_mem_index; + + s->set_irq = set_irq; + s->irq_opaque = irq_opaque; + s->irq = irq; + + s->timers[0].index = 0; + s->timers[0].timer = qemu_new_timer(vm_clock, cuda_timer1, s); + s->timers[0].latch = 0xffff; + set_counter(s, &s->timers[0], 0xffff); + + s->timers[1].index = 1; + s->timers[1].latch = 0; + // s->ier = T1_INT | SR_INT; + s->ier = 0; + set_counter(s, &s->timers[1], 0xffff); + + s->adb_poll_timer = qemu_new_timer(vm_clock, cuda_adb_poll, s); + cuda_mem_index = cpu_register_io_memory(0, cuda_read, cuda_write, s); + return cuda_mem_index; +} diff --git a/tools/ioemu/hw/dma.c b/tools/ioemu/hw/dma.c new file mode 100644 index 0000000000..ea13eae492 --- /dev/null +++ b/tools/ioemu/hw/dma.c @@ -0,0 +1,537 @@ +/* + * QEMU DMA emulation + * + * Copyright (c) 2003-2004 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* #define DEBUG_DMA */ + +#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__) +#ifdef DEBUG_DMA +#define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__) +#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__) +#define ldebug(...) fprintf (stderr, "dma: " __VA_ARGS__) +#else +#define lwarn(...) +#define linfo(...) +#define ldebug(...) +#endif + +#define LENOFA(a) ((int) (sizeof(a)/sizeof(a[0]))) + +struct dma_regs { + int now[2]; + uint16_t base[2]; + uint8_t mode; + uint8_t page; + uint8_t pageh; + uint8_t dack; + uint8_t eop; + DMA_transfer_handler transfer_handler; + void *opaque; +}; + +#define ADDR 0 +#define COUNT 1 + +static struct dma_cont { + uint8_t status; + uint8_t command; + uint8_t mask; + uint8_t flip_flop; + int dshift; + struct dma_regs regs[4]; +} dma_controllers[2]; + +enum { + CMD_MEMORY_TO_MEMORY = 0x01, + CMD_FIXED_ADDRESS = 0x02, + CMD_BLOCK_CONTROLLER = 0x04, + CMD_COMPRESSED_TIME = 0x08, + CMD_CYCLIC_PRIORITY = 0x10, + CMD_EXTENDED_WRITE = 0x20, + CMD_LOW_DREQ = 0x40, + CMD_LOW_DACK = 0x80, + CMD_NOT_SUPPORTED = CMD_MEMORY_TO_MEMORY | CMD_FIXED_ADDRESS + | CMD_COMPRESSED_TIME | CMD_CYCLIC_PRIORITY | CMD_EXTENDED_WRITE + | CMD_LOW_DREQ | CMD_LOW_DACK + +}; + +static int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0}; + +static void write_page (void *opaque, uint32_t nport, uint32_t data) +{ + struct dma_cont *d = opaque; + int ichan; + + ichan = channels[nport & 7]; + if (-1 == ichan) { + dolog ("invalid channel %#x %#x\n", nport, data); + return; + } + d->regs[ichan].page = data; +} + +static void write_pageh (void *opaque, uint32_t nport, uint32_t data) +{ + struct dma_cont *d = opaque; + int ichan; + + ichan = channels[nport & 7]; + if (-1 == ichan) { + dolog ("invalid channel %#x %#x\n", nport, data); + return; + } + d->regs[ichan].pageh = data; +} + +static uint32_t read_page (void *opaque, uint32_t nport) +{ + struct dma_cont *d = opaque; + int ichan; + + ichan = channels[nport & 7]; + if (-1 == ichan) { + dolog ("invalid channel read %#x\n", nport); + return 0; + } + return d->regs[ichan].page; +} + +static uint32_t read_pageh (void *opaque, uint32_t nport) +{ + struct dma_cont *d = opaque; + int ichan; + + ichan = channels[nport & 7]; + if (-1 == ichan) { + dolog ("invalid channel read %#x\n", nport); + return 0; + } + return d->regs[ichan].pageh; +} + +static inline void init_chan (struct dma_cont *d, int ichan) +{ + struct dma_regs *r; + + r = d->regs + ichan; + r->now[ADDR] = r->base[ADDR] << d->dshift; + r->now[COUNT] = 0; +} + +static inline int getff (struct dma_cont *d) +{ + int ff; + + ff = d->flip_flop; + d->flip_flop = !ff; + return ff; +} + +static uint32_t read_chan (void *opaque, uint32_t nport) +{ + struct dma_cont *d = opaque; + int ichan, nreg, iport, ff, val, dir; + struct dma_regs *r; + + iport = (nport >> d->dshift) & 0x0f; + ichan = iport >> 1; + nreg = iport & 1; + r = d->regs + ichan; + + dir = ((r->mode >> 5) & 1) ? -1 : 1; + ff = getff (d); + if (nreg) + val = (r->base[COUNT] << d->dshift) - r->now[COUNT]; + else + val = r->now[ADDR] + r->now[COUNT] * dir; + + ldebug ("read_chan %#x -> %d\n", iport, val); + return (val >> (d->dshift + (ff << 3))) & 0xff; +} + +static void write_chan (void *opaque, uint32_t nport, uint32_t data) +{ + struct dma_cont *d = opaque; + int iport, ichan, nreg; + struct dma_regs *r; + + iport = (nport >> d->dshift) & 0x0f; + ichan = iport >> 1; + nreg = iport & 1; + r = d->regs + ichan; + if (getff (d)) { + r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00); + init_chan (d, ichan); + } else { + r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff); + } +} + +static void write_cont (void *opaque, uint32_t nport, uint32_t data) +{ + struct dma_cont *d = opaque; + int iport, ichan = 0; + + iport = (nport >> d->dshift) & 0x0f; + switch (iport) { + case 0x08: /* command */ + if ((data != 0) && (data & CMD_NOT_SUPPORTED)) { + dolog ("command %#x not supported\n", data); + return; + } + d->command = data; + break; + + case 0x09: + ichan = data & 3; + if (data & 4) { + d->status |= 1 << (ichan + 4); + } + else { + d->status &= ~(1 << (ichan + 4)); + } + d->status &= ~(1 << ichan); + break; + + case 0x0a: /* single mask */ + if (data & 4) + d->mask |= 1 << (data & 3); + else + d->mask &= ~(1 << (data & 3)); + break; + + case 0x0b: /* mode */ + { + ichan = data & 3; +#ifdef DEBUG_DMA + { + int op, ai, dir, opmode; + op = (data >> 2) & 3; + ai = (data >> 4) & 1; + dir = (data >> 5) & 1; + opmode = (data >> 6) & 3; + + linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n", + ichan, op, ai, dir, opmode); + } +#endif + d->regs[ichan].mode = data; + break; + } + + case 0x0c: /* clear flip flop */ + d->flip_flop = 0; + break; + + case 0x0d: /* reset */ + d->flip_flop = 0; + d->mask = ~0; + d->status = 0; + d->command = 0; + break; + + case 0x0e: /* clear mask for all channels */ + d->mask = 0; + break; + + case 0x0f: /* write mask for all channels */ + d->mask = data; + break; + + default: + dolog ("unknown iport %#x\n", iport); + break; + } + +#ifdef DEBUG_DMA + if (0xc != iport) { + linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n", + nport, ichan, data); + } +#endif +} + +static uint32_t read_cont (void *opaque, uint32_t nport) +{ + struct dma_cont *d = opaque; + int iport, val; + + iport = (nport >> d->dshift) & 0x0f; + switch (iport) { + case 0x08: /* status */ + val = d->status; + d->status &= 0xf0; + break; + case 0x0f: /* mask */ + val = d->mask; + break; + default: + val = 0; + break; + } + + ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val); + return val; +} + +int DMA_get_channel_mode (int nchan) +{ + return dma_controllers[nchan > 3].regs[nchan & 3].mode; +} + +void DMA_hold_DREQ (int nchan) +{ + int ncont, ichan; + + ncont = nchan > 3; + ichan = nchan & 3; + linfo ("held cont=%d chan=%d\n", ncont, ichan); + dma_controllers[ncont].status |= 1 << (ichan + 4); +} + +void DMA_release_DREQ (int nchan) +{ + int ncont, ichan; + + ncont = nchan > 3; + ichan = nchan & 3; + linfo ("released cont=%d chan=%d\n", ncont, ichan); + dma_controllers[ncont].status &= ~(1 << (ichan + 4)); +} + +static void channel_run (int ncont, int ichan) +{ + int n; + struct dma_regs *r = &dma_controllers[ncont].regs[ichan]; +#ifdef DEBUG_DMA + int dir, opmode; + + dir = (r->mode >> 5) & 1; + opmode = (r->mode >> 6) & 3; + + if (dir) { + dolog ("DMA in address decrement mode\n"); + } + if (opmode != 1) { + dolog ("DMA not in single mode select %#x\n", opmode); + } +#endif + + r = dma_controllers[ncont].regs + ichan; + n = r->transfer_handler (r->opaque, ichan + (ncont << 2), + r->now[COUNT], (r->base[COUNT] + 1) << ncont); + r->now[COUNT] = n; + ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont); +} + +void DMA_run (void) +{ + struct dma_cont *d; + int icont, ichan; + + d = dma_controllers; + + for (icont = 0; icont < 2; icont++, d++) { + for (ichan = 0; ichan < 4; ichan++) { + int mask; + + mask = 1 << ichan; + + if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) + channel_run (icont, ichan); + } + } +} + +void DMA_register_channel (int nchan, + DMA_transfer_handler transfer_handler, + void *opaque) +{ + struct dma_regs *r; + int ichan, ncont; + + ncont = nchan > 3; + ichan = nchan & 3; + + r = dma_controllers[ncont].regs + ichan; + r->transfer_handler = transfer_handler; + r->opaque = opaque; +} + +int DMA_read_memory (int nchan, void *buf, int pos, int len) +{ + struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; + target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; + + if (r->mode & 0x20) { + int i; + uint8_t *p = buf; + + cpu_physical_memory_read (addr - pos - len, buf, len); + /* What about 16bit transfers? */ + for (i = 0; i < len >> 1; i++) { + uint8_t b = p[len - i - 1]; + p[i] = b; + } + } + else + cpu_physical_memory_read (addr + pos, buf, len); + + return len; +} + +int DMA_write_memory (int nchan, void *buf, int pos, int len) +{ + struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; + target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; + + if (r->mode & 0x20) { + int i; + uint8_t *p = buf; + + cpu_physical_memory_write (addr - pos - len, buf, len); + /* What about 16bit transfers? */ + for (i = 0; i < len; i++) { + uint8_t b = p[len - i - 1]; + p[i] = b; + } + } + else + cpu_physical_memory_write (addr + pos, buf, len); + + return len; +} + +/* request the emulator to transfer a new DMA memory block ASAP */ +void DMA_schedule(int nchan) +{ + CPUState *env = cpu_single_env; + if (env) + cpu_interrupt(env, CPU_INTERRUPT_EXIT); +} + +static void dma_reset(void *opaque) +{ + struct dma_cont *d = opaque; + write_cont (d, (0x0d << d->dshift), 0); +} + +/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */ +static void dma_init2(struct dma_cont *d, int base, int dshift, + int page_base, int pageh_base) +{ + const static int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 }; + int i; + + d->dshift = dshift; + for (i = 0; i < 8; i++) { + register_ioport_write (base + (i << dshift), 1, 1, write_chan, d); + register_ioport_read (base + (i << dshift), 1, 1, read_chan, d); + } + for (i = 0; i < LENOFA (page_port_list); i++) { + register_ioport_write (page_base + page_port_list[i], 1, 1, + write_page, d); + register_ioport_read (page_base + page_port_list[i], 1, 1, + read_page, d); + if (pageh_base >= 0) { + register_ioport_write (pageh_base + page_port_list[i], 1, 1, + write_pageh, d); + register_ioport_read (pageh_base + page_port_list[i], 1, 1, + read_pageh, d); + } + } + for (i = 0; i < 8; i++) { + register_ioport_write (base + ((i + 8) << dshift), 1, 1, + write_cont, d); + register_ioport_read (base + ((i + 8) << dshift), 1, 1, + read_cont, d); + } + qemu_register_reset(dma_reset, d); + dma_reset(d); +} + +static void dma_save (QEMUFile *f, void *opaque) +{ + struct dma_cont *d = opaque; + int i; + + /* qemu_put_8s (f, &d->status); */ + qemu_put_8s (f, &d->command); + qemu_put_8s (f, &d->mask); + qemu_put_8s (f, &d->flip_flop); + qemu_put_be32s (f, &d->dshift); + + for (i = 0; i < 4; ++i) { + struct dma_regs *r = &d->regs[i]; + qemu_put_be32s (f, &r->now[0]); + qemu_put_be32s (f, &r->now[1]); + qemu_put_be16s (f, &r->base[0]); + qemu_put_be16s (f, &r->base[1]); + qemu_put_8s (f, &r->mode); + qemu_put_8s (f, &r->page); + qemu_put_8s (f, &r->pageh); + qemu_put_8s (f, &r->dack); + qemu_put_8s (f, &r->eop); + } +} + +static int dma_load (QEMUFile *f, void *opaque, int version_id) +{ + struct dma_cont *d = opaque; + int i; + + if (version_id != 1) + return -EINVAL; + + /* qemu_get_8s (f, &d->status); */ + qemu_get_8s (f, &d->command); + qemu_get_8s (f, &d->mask); + qemu_get_8s (f, &d->flip_flop); + qemu_get_be32s (f, &d->dshift); + + for (i = 0; i < 4; ++i) { + struct dma_regs *r = &d->regs[i]; + qemu_get_be32s (f, &r->now[0]); + qemu_get_be32s (f, &r->now[1]); + qemu_get_be16s (f, &r->base[0]); + qemu_get_be16s (f, &r->base[1]); + qemu_get_8s (f, &r->mode); + qemu_get_8s (f, &r->page); + qemu_get_8s (f, &r->pageh); + qemu_get_8s (f, &r->dack); + qemu_get_8s (f, &r->eop); + } + return 0; +} + +void DMA_init (int high_page_enable) +{ + dma_init2(&dma_controllers[0], 0x00, 0, 0x80, + high_page_enable ? 0x480 : -1); + dma_init2(&dma_controllers[1], 0xc0, 1, 0x88, + high_page_enable ? 0x488 : -1); + register_savevm ("dma", 0, 1, dma_save, dma_load, &dma_controllers[0]); + register_savevm ("dma", 1, 1, dma_save, dma_load, &dma_controllers[1]); +} diff --git a/tools/ioemu/hw/es1370.c b/tools/ioemu/hw/es1370.c new file mode 100644 index 0000000000..9fddd9d8b3 --- /dev/null +++ b/tools/ioemu/hw/es1370.c @@ -0,0 +1,1062 @@ +/* + * QEMU ES1370 emulation + * + * Copyright (c) 2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* #define DEBUG_ES1370 */ +/* #define VERBOSE_ES1370 */ +#define SILENT_ES1370 + +#include "vl.h" + +/* Missing stuff: + SCTRL_P[12](END|ST)INC + SCTRL_P1SCTRLD + SCTRL_P2DACSEN + CTRL_DAC_SYNC + MIDI + non looped mode + surely more +*/ + +/* + Following macros and samplerate array were copied verbatim from + Linux kernel 2.4.30: drivers/sound/es1370.c + + Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch) +*/ + +/* Start blatant GPL violation */ + +#define ES1370_REG_CONTROL 0x00 +#define ES1370_REG_STATUS 0x04 +#define ES1370_REG_UART_DATA 0x08 +#define ES1370_REG_UART_STATUS 0x09 +#define ES1370_REG_UART_CONTROL 0x09 +#define ES1370_REG_UART_TEST 0x0a +#define ES1370_REG_MEMPAGE 0x0c +#define ES1370_REG_CODEC 0x10 +#define ES1370_REG_SERIAL_CONTROL 0x20 +#define ES1370_REG_DAC1_SCOUNT 0x24 +#define ES1370_REG_DAC2_SCOUNT 0x28 +#define ES1370_REG_ADC_SCOUNT 0x2c + +#define ES1370_REG_DAC1_FRAMEADR 0xc30 +#define ES1370_REG_DAC1_FRAMECNT 0xc34 +#define ES1370_REG_DAC2_FRAMEADR 0xc38 +#define ES1370_REG_DAC2_FRAMECNT 0xc3c +#define ES1370_REG_ADC_FRAMEADR 0xd30 +#define ES1370_REG_ADC_FRAMECNT 0xd34 +#define ES1370_REG_PHANTOM_FRAMEADR 0xd38 +#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c + +static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 }; + +#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2) +#define DAC2_DIVTOSR(x) (1411200/((x)+2)) + +#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */ +#define CTRL_XCTL1 0x40000000 /* electret mic bias */ +#define CTRL_OPEN 0x20000000 /* no function, can be read and written */ +#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */ +#define CTRL_SH_PCLKDIV 16 +#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ +#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */ +#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */ +#define CTRL_SH_WTSRSEL 12 +#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */ +#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ +#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 = MPEG */ +#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */ +#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ +#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ +#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ +#define CTRL_ADC_EN 0x00000010 /* enable ADC */ +#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ +#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably at address 0x200) */ +#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */ +#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */ + +#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ +#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in progress */ +#define STAT_CBUSY 0x00000200 /* 1 = codec busy */ +#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */ +#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ +#define STAT_SH_VC 5 +#define STAT_MCCB 0x00000010 /* CCB int pending */ +#define STAT_UART 0x00000008 /* UART int pending */ +#define STAT_DAC1 0x00000004 /* DAC1 int pending */ +#define STAT_DAC2 0x00000002 /* DAC2 int pending */ +#define STAT_ADC 0x00000001 /* ADC int pending */ + +#define USTAT_RXINT 0x80 /* UART rx int pending */ +#define USTAT_TXINT 0x04 /* UART tx int pending */ +#define USTAT_TXRDY 0x02 /* UART tx ready */ +#define USTAT_RXRDY 0x01 /* UART rx ready */ + +#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ +#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ +#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ +#define UCTRL_CNTRL 0x03 /* control field */ +#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ + +#define SCTRL_P2ENDINC 0x00380000 /* */ +#define SCTRL_SH_P2ENDINC 19 +#define SCTRL_P2STINC 0x00070000 /* */ +#define SCTRL_SH_P2STINC 16 +#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ +#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ +#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ +#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ +#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ +#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ +#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ +#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ +#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ +#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ +#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ +#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ +#define SCTRL_R1FMT 0x00000030 /* format mask */ +#define SCTRL_SH_R1FMT 4 +#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ +#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ +#define SCTRL_P2FMT 0x0000000c /* format mask */ +#define SCTRL_SH_P2FMT 2 +#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ +#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ +#define SCTRL_P1FMT 0x00000003 /* format mask */ +#define SCTRL_SH_P1FMT 0 + +/* End blatant GPL violation */ + +#define NB_CHANNELS 3 +#define DAC1_CHANNEL 0 +#define DAC2_CHANNEL 1 +#define ADC_CHANNEL 2 + +#define IO_READ_PROTO(n) \ +static uint32_t n (void *opaque, uint32_t addr) +#define IO_WRITE_PROTO(n) \ +static void n (void *opaque, uint32_t addr, uint32_t val) + +static void es1370_dac1_callback (void *opaque, int free); +static void es1370_dac2_callback (void *opaque, int free); +static void es1370_adc_callback (void *opaque, int avail); + +#ifdef DEBUG_ES1370 + +#define ldebug(...) AUD_log ("es1370", __VA_ARGS__) + +static void print_ctl (uint32_t val) +{ + char buf[1024]; + + buf[0] = '\0'; +#define a(n) if (val & CTRL_##n) strcat (buf, " "#n) + a (ADC_STOP); + a (XCTL1); + a (OPEN); + a (MSFMTSEL); + a (M_SBB); + a (DAC_SYNC); + a (CCB_INTRM); + a (M_CB); + a (XCTL0); + a (BREQ); + a (DAC1_EN); + a (DAC2_EN); + a (ADC_EN); + a (UART_EN); + a (JYSTK_EN); + a (CDC_EN); + a (SERR_DIS); +#undef a + AUD_log ("es1370", "ctl - PCLKDIV %d(DAC2 freq %d), freq %d,%s\n", + (val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV, + DAC2_DIVTOSR ((val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), + dac1_samplerate[(val & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], + buf); +} + +static void print_sctl (uint32_t val) +{ + static const char *fmt_names[] = {"8M", "8S", "16M", "16S"}; + char buf[1024]; + + buf[0] = '\0'; + +#define a(n) if (val & SCTRL_##n) strcat (buf, " "#n) +#define b(n) if (!(val & SCTRL_##n)) strcat (buf, " "#n) + b (R1LOOPSEL); + b (P2LOOPSEL); + b (P1LOOPSEL); + a (P2PAUSE); + a (P1PAUSE); + a (R1INTEN); + a (P2INTEN); + a (P1INTEN); + a (P1SCTRLD); + a (P2DACSEN); + if (buf[0]) { + strcat (buf, "\n "); + } + else { + buf[0] = ' '; + buf[1] = '\0'; + } +#undef b +#undef a + AUD_log ("es1370", + "%s" + "p2_end_inc %d, p2_st_inc %d, r1_fmt %s, p2_fmt %s, p1_fmt %s\n", + buf, + (val & SCTRL_P2ENDINC) >> SCTRL_SH_P2ENDINC, + (val & SCTRL_P2STINC) >> SCTRL_SH_P2STINC, + fmt_names [(val >> SCTRL_SH_R1FMT) & 3], + fmt_names [(val >> SCTRL_SH_P2FMT) & 3], + fmt_names [(val >> SCTRL_SH_P1FMT) & 3] + ); +} +#else +#define ldebug(...) +#define print_ctl(...) +#define print_sctl(...) +#endif + +#ifdef VERBOSE_ES1370 +#define dolog(...) AUD_log ("es1370", __VA_ARGS__) +#else +#define dolog(...) +#endif + +#ifndef SILENT_ES1370 +#define lwarn(...) AUD_log ("es1370: warning", __VA_ARGS__) +#else +#define lwarn(...) +#endif + +struct chan { + uint32_t shift; + uint32_t leftover; + uint32_t scount; + uint32_t frame_addr; + uint32_t frame_cnt; +}; + +typedef struct ES1370State { + PCIDevice *pci_dev; + + QEMUSoundCard card; + struct chan chan[NB_CHANNELS]; + SWVoiceOut *dac_voice[2]; + SWVoiceIn *adc_voice; + + uint32_t ctl; + uint32_t status; + uint32_t mempage; + uint32_t codec; + uint32_t sctl; +} ES1370State; + +typedef struct PCIES1370State { + PCIDevice dev; + ES1370State es1370; +} PCIES1370State; + +struct chan_bits { + uint32_t ctl_en; + uint32_t stat_int; + uint32_t sctl_pause; + uint32_t sctl_inten; + uint32_t sctl_fmt; + uint32_t sctl_sh_fmt; + uint32_t sctl_loopsel; + void (*calc_freq) (ES1370State *s, uint32_t ctl, + uint32_t *old_freq, uint32_t *new_freq); +}; + +static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl, + uint32_t *old_freq, uint32_t *new_freq); +static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl, + uint32_t *old_freq, + uint32_t *new_freq); + +static const struct chan_bits es1370_chan_bits[] = { + {CTRL_DAC1_EN, STAT_DAC1, SCTRL_P1PAUSE, SCTRL_P1INTEN, + SCTRL_P1FMT, SCTRL_SH_P1FMT, SCTRL_P1LOOPSEL, + es1370_dac1_calc_freq}, + + {CTRL_DAC2_EN, STAT_DAC2, SCTRL_P2PAUSE, SCTRL_P2INTEN, + SCTRL_P2FMT, SCTRL_SH_P2FMT, SCTRL_P2LOOPSEL, + es1370_dac2_and_adc_calc_freq}, + + {CTRL_ADC_EN, STAT_ADC, 0, SCTRL_R1INTEN, + SCTRL_R1FMT, SCTRL_SH_R1FMT, SCTRL_R1LOOPSEL, + es1370_dac2_and_adc_calc_freq} +}; + +static void es1370_update_status (ES1370State *s, uint32_t new_status) +{ + uint32_t level = new_status & (STAT_DAC1 | STAT_DAC2 | STAT_ADC); + + if (level) { + s->status = new_status | STAT_INTR; + } + else { + s->status = new_status & ~STAT_INTR; + } + pci_set_irq (s->pci_dev, 0, !!level); +} + +static void es1370_reset (ES1370State *s) +{ + size_t i; + + s->ctl = 1; + s->status = 0x60; + s->mempage = 0; + s->codec = 0; + s->sctl = 0; + + for (i = 0; i < NB_CHANNELS; ++i) { + struct chan *d = &s->chan[i]; + d->scount = 0; + d->leftover = 0; + if (i == ADC_CHANNEL) { + AUD_close_in (&s->card, s->adc_voice); + s->adc_voice = NULL; + } + else { + AUD_close_out (&s->card, s->dac_voice[i]); + s->dac_voice[i] = NULL; + } + } + pci_set_irq (s->pci_dev, 0, 0); +} + +static void es1370_maybe_lower_irq (ES1370State *s, uint32_t sctl) +{ + uint32_t new_status = s->status; + + if (!(sctl & SCTRL_P1INTEN) && (s->sctl & SCTRL_P1INTEN)) { + new_status &= ~STAT_DAC1; + } + + if (!(sctl & SCTRL_P2INTEN) && (s->sctl & SCTRL_P2INTEN)) { + new_status &= ~STAT_DAC2; + } + + if (!(sctl & SCTRL_R1INTEN) && (s->sctl & SCTRL_R1INTEN)) { + new_status &= ~STAT_ADC; + } + + if (new_status != s->status) { + es1370_update_status (s, new_status); + } +} + +static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl, + uint32_t *old_freq, uint32_t *new_freq) + +{ + *old_freq = dac1_samplerate[(s->ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; + *new_freq = dac1_samplerate[(ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; +} + +static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl, + uint32_t *old_freq, + uint32_t *new_freq) + +{ + uint32_t old_pclkdiv, new_pclkdiv; + + new_pclkdiv = (ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV; + old_pclkdiv = (s->ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV; + *new_freq = DAC2_DIVTOSR (new_pclkdiv); + *old_freq = DAC2_DIVTOSR (old_pclkdiv); +} + +static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl) +{ + size_t i; + uint32_t old_freq, new_freq, old_fmt, new_fmt; + + for (i = 0; i < NB_CHANNELS; ++i) { + struct chan *d = &s->chan[i]; + const struct chan_bits *b = &es1370_chan_bits[i]; + + new_fmt = (sctl & b->sctl_fmt) >> b->sctl_sh_fmt; + old_fmt = (s->sctl & b->sctl_fmt) >> b->sctl_sh_fmt; + + b->calc_freq (s, ctl, &old_freq, &new_freq); + + if ((old_fmt != new_fmt) || (old_freq != new_freq)) { + d->shift = (new_fmt & 1) + (new_fmt >> 1); + ldebug ("channel %d, freq = %d, nchannels %d, fmt %d, shift %d\n", + i, + new_freq, + 1 << (new_fmt & 1), + (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8, + d->shift); + if (new_freq) { + audsettings_t as; + + as.freq = new_freq; + as.nchannels = 1 << (new_fmt & 1); + as.fmt = (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8; + + if (i == ADC_CHANNEL) { + s->adc_voice = + AUD_open_in ( + &s->card, + s->adc_voice, + "es1370.adc", + s, + es1370_adc_callback, + &as, + 0 /* little endian */ + ); + } + else { + s->dac_voice[i] = + AUD_open_out ( + &s->card, + s->dac_voice[i], + i ? "es1370.dac2" : "es1370.dac1", + s, + i ? es1370_dac2_callback : es1370_dac1_callback, + &as, + 0 /* litle endian */ + ); + } + } + } + + if (((ctl ^ s->ctl) & b->ctl_en) + || ((sctl ^ s->sctl) & b->sctl_pause)) { + int on = (ctl & b->ctl_en) && !(sctl & b->sctl_pause); + + if (i == ADC_CHANNEL) { + AUD_set_active_in (s->adc_voice, on); + } + else { + AUD_set_active_out (s->dac_voice[i], on); + } + } + } + + s->ctl = ctl; + s->sctl = sctl; +} + +static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr) +{ + addr &= 0xff; + if (addr >= 0x30 && addr <= 0x3f) + addr |= s->mempage << 8; + return addr; +} + +IO_WRITE_PROTO (es1370_writeb) +{ + ES1370State *s = opaque; + addr = es1370_fixup (s, addr); + uint32_t shift, mask; + + switch (addr) { + case ES1370_REG_CONTROL: + case ES1370_REG_CONTROL + 1: + case ES1370_REG_CONTROL + 2: + case ES1370_REG_CONTROL + 3: + shift = (addr - ES1370_REG_CONTROL) << 3; + mask = 0xff << shift; + val = (s->ctl & ~mask) | ((val & 0xff) << shift); + es1370_update_voices (s, val, s->sctl); + print_ctl (val); + break; + case ES1370_REG_MEMPAGE: + s->mempage = val; + break; + case ES1370_REG_SERIAL_CONTROL: + case ES1370_REG_SERIAL_CONTROL + 1: + case ES1370_REG_SERIAL_CONTROL + 2: + case ES1370_REG_SERIAL_CONTROL + 3: + shift = (addr - ES1370_REG_SERIAL_CONTROL) << 3; + mask = 0xff << shift; + val = (s->sctl & ~mask) | ((val & 0xff) << shift); + es1370_maybe_lower_irq (s, val); + es1370_update_voices (s, s->ctl, val); + print_sctl (val); + break; + default: + lwarn ("writeb %#x <- %#x\n", addr, val); + break; + } +} + +IO_WRITE_PROTO (es1370_writew) +{ + ES1370State *s = opaque; + addr = es1370_fixup (s, addr); + uint32_t shift, mask; + struct chan *d = &s->chan[0]; + + switch (addr) { + case ES1370_REG_CODEC: + dolog ("ignored codec write address %#x, data %#x\n", + (val >> 8) & 0xff, val & 0xff); + s->codec = val; + break; + + case ES1370_REG_CONTROL: + case ES1370_REG_CONTROL + 2: + shift = (addr != ES1370_REG_CONTROL) << 4; + mask = 0xffff << shift; + val = (s->ctl & ~mask) | ((val & 0xffff) << shift); + es1370_update_voices (s, val, s->sctl); + print_ctl (val); + break; + + case ES1370_REG_ADC_SCOUNT: + d++; + case ES1370_REG_DAC2_SCOUNT: + d++; + case ES1370_REG_DAC1_SCOUNT: + d->scount = (d->scount & ~0xffff) | (val & 0xffff); + break; + + default: + lwarn ("writew %#x <- %#x\n", addr, val); + break; + } +} + +IO_WRITE_PROTO (es1370_writel) +{ + ES1370State *s = opaque; + struct chan *d = &s->chan[0]; + + addr = es1370_fixup (s, addr); + + switch (addr) { + case ES1370_REG_CONTROL: + es1370_update_voices (s, val, s->sctl); + print_ctl (val); + break; + + case ES1370_REG_MEMPAGE: + s->mempage = val & 0xf; + break; + + case ES1370_REG_SERIAL_CONTROL: + es1370_maybe_lower_irq (s, val); + es1370_update_voices (s, s->ctl, val); + print_sctl (val); + break; + + case ES1370_REG_ADC_SCOUNT: + d++; + case ES1370_REG_DAC2_SCOUNT: + d++; + case ES1370_REG_DAC1_SCOUNT: + d->scount = (val & 0xffff) | (d->scount & ~0xffff); + ldebug ("chan %d CURR_SAMP_CT %d, SAMP_CT %d\n", + d - &s->chan[0], val >> 16, (val & 0xffff)); + break; + + case ES1370_REG_ADC_FRAMEADR: + d++; + case ES1370_REG_DAC2_FRAMEADR: + d++; + case ES1370_REG_DAC1_FRAMEADR: + d->frame_addr = val; + ldebug ("chan %d frame address %#x\n", d - &s->chan[0], val); + break; + + case ES1370_REG_PHANTOM_FRAMECNT: + lwarn ("writing to phantom frame count %#x\n", val); + break; + case ES1370_REG_PHANTOM_FRAMEADR: + lwarn ("writing to phantom frame address %#x\n", val); + break; + + case ES1370_REG_ADC_FRAMECNT: + d++; + case ES1370_REG_DAC2_FRAMECNT: + d++; + case ES1370_REG_DAC1_FRAMECNT: + d->frame_cnt = val; + d->leftover = 0; + ldebug ("chan %d frame count %d, buffer size %d\n", + d - &s->chan[0], val >> 16, val & 0xffff); + break; + + default: + lwarn ("writel %#x <- %#x\n", addr, val); + break; + } +} + +IO_READ_PROTO (es1370_readb) +{ + ES1370State *s = opaque; + uint32_t val; + + addr = es1370_fixup (s, addr); + + switch (addr) { + case 0x1b: /* Legacy */ + lwarn ("Attempt to read from legacy register\n"); + val = 5; + break; + case ES1370_REG_MEMPAGE: + val = s->mempage; + break; + case ES1370_REG_CONTROL + 0: + case ES1370_REG_CONTROL + 1: + case ES1370_REG_CONTROL + 2: + case ES1370_REG_CONTROL + 3: + val = s->ctl >> ((addr - ES1370_REG_CONTROL) << 3); + break; + case ES1370_REG_STATUS + 0: + case ES1370_REG_STATUS + 1: + case ES1370_REG_STATUS + 2: + case ES1370_REG_STATUS + 3: + val = s->status >> ((addr - ES1370_REG_STATUS) << 3); + break; + default: + val = ~0; + lwarn ("readb %#x -> %#x\n", addr, val); + break; + } + return val; +} + +IO_READ_PROTO (es1370_readw) +{ + ES1370State *s = opaque; + struct chan *d = &s->chan[0]; + uint32_t val; + + addr = es1370_fixup (s, addr); + + switch (addr) { + case ES1370_REG_ADC_SCOUNT + 2: + d++; + case ES1370_REG_DAC2_SCOUNT + 2: + d++; + case ES1370_REG_DAC1_SCOUNT + 2: + val = d->scount >> 16; + break; + + case ES1370_REG_ADC_FRAMECNT: + d++; + case ES1370_REG_DAC2_FRAMECNT: + d++; + case ES1370_REG_DAC1_FRAMECNT: + val = d->frame_cnt & 0xffff; + break; + + case ES1370_REG_ADC_FRAMECNT + 2: + d++; + case ES1370_REG_DAC2_FRAMECNT + 2: + d++; + case ES1370_REG_DAC1_FRAMECNT + 2: + val = d->frame_cnt >> 16; + break; + + default: + val = ~0; + lwarn ("readw %#x -> %#x\n", addr, val); + break; + } + + return val; +} + +IO_READ_PROTO (es1370_readl) +{ + ES1370State *s = opaque; + uint32_t val; + struct chan *d = &s->chan[0]; + + addr = es1370_fixup (s, addr); + + switch (addr) { + case ES1370_REG_CONTROL: + val = s->ctl; + break; + case ES1370_REG_STATUS: + val = s->status; + break; + case ES1370_REG_MEMPAGE: + val = s->mempage; + break; + case ES1370_REG_CODEC: + val = s->codec; + break; + case ES1370_REG_SERIAL_CONTROL: + val = s->sctl; + break; + + case ES1370_REG_ADC_SCOUNT: + d++; + case ES1370_REG_DAC2_SCOUNT: + d++; + case ES1370_REG_DAC1_SCOUNT: + val = d->scount; +#ifdef DEBUG_ES1370 + { + uint32_t curr_count = d->scount >> 16; + uint32_t count = d->scount & 0xffff; + + curr_count <<= d->shift; + count <<= d->shift; + dolog ("read scount curr %d, total %d\n", curr_count, count); + } +#endif + break; + + case ES1370_REG_ADC_FRAMECNT: + d++; + case ES1370_REG_DAC2_FRAMECNT: + d++; + case ES1370_REG_DAC1_FRAMECNT: + val = d->frame_cnt; +#ifdef DEBUG_ES1370 + { + uint32_t size = ((d->frame_cnt & 0xffff) + 1) << 2; + uint32_t curr = ((d->frame_cnt >> 16) + 1) << 2; + if (curr > size) + dolog ("read framecnt curr %d, size %d %d\n", curr, size, + curr > size); + } +#endif + break; + + case ES1370_REG_ADC_FRAMEADR: + d++; + case ES1370_REG_DAC2_FRAMEADR: + d++; + case ES1370_REG_DAC1_FRAMEADR: + val = d->frame_addr; + break; + + case ES1370_REG_PHANTOM_FRAMECNT: + val = ~0U; + lwarn ("reading from phantom frame count\n"); + break; + case ES1370_REG_PHANTOM_FRAMEADR: + val = ~0U; + lwarn ("reading from phantom frame address\n"); + break; + + default: + val = ~0U; + lwarn ("readl %#x -> %#x\n", addr, val); + break; + } + return val; +} + + +static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel, + int max, int *irq) +{ + uint8_t tmpbuf[4096]; + uint32_t addr = d->frame_addr; + int sc = d->scount & 0xffff; + int csc = d->scount >> 16; + int csc_bytes = (csc + 1) << d->shift; + int cnt = d->frame_cnt >> 16; + int size = d->frame_cnt & 0xffff; + int left = ((size - cnt + 1) << 2) + d->leftover; + int transfered = 0; + int temp = audio_MIN (max, audio_MIN (left, csc_bytes)); + int index = d - &s->chan[0]; + + addr += (cnt << 2) + d->leftover; + + if (index == ADC_CHANNEL) { + while (temp) { + int acquired, to_copy; + + to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf)); + acquired = AUD_read (s->adc_voice, tmpbuf, to_copy); + if (!acquired) + break; + + cpu_physical_memory_write (addr, tmpbuf, acquired); + + temp -= acquired; + addr += acquired; + transfered += acquired; + } + } + else { + SWVoiceOut *voice = s->dac_voice[index]; + + while (temp) { + int copied, to_copy; + + to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf)); + cpu_physical_memory_read (addr, tmpbuf, to_copy); + copied = AUD_write (voice, tmpbuf, to_copy); + if (!copied) + break; + temp -= copied; + addr += copied; + transfered += copied; + } + } + + if (csc_bytes == transfered) { + *irq = 1; + d->scount = sc | (sc << 16); + ldebug ("sc = %d, rate = %f\n", + (sc + 1) << d->shift, + (sc + 1) / (double) 44100); + } + else { + *irq = 0; + d->scount = sc | (((csc_bytes - transfered - 1) >> d->shift) << 16); + } + + cnt += (transfered + d->leftover) >> 2; + + if (s->sctl & loop_sel) { + /* Bah, how stupid is that having a 0 represent true value? + i just spent few hours on this shit */ + AUD_log ("es1370: warning", "non looping mode\n"); + } + else { + d->frame_cnt = size; + + if ((uint32_t) cnt <= d->frame_cnt) + d->frame_cnt |= cnt << 16; + } + + d->leftover = (transfered + d->leftover) & 3; +} + +static void es1370_run_channel (ES1370State *s, size_t chan, int free_or_avail) +{ + uint32_t new_status = s->status; + int max_bytes, irq; + struct chan *d = &s->chan[chan]; + const struct chan_bits *b = &es1370_chan_bits[chan]; + + if (!(s->ctl & b->ctl_en) || (s->sctl & b->sctl_pause)) { + return; + } + + max_bytes = free_or_avail; + max_bytes &= ~((1 << d->shift) - 1); + if (!max_bytes) { + return; + } + + es1370_transfer_audio (s, d, b->sctl_loopsel, max_bytes, &irq); + + if (irq) { + if (s->sctl & b->sctl_inten) { + new_status |= b->stat_int; + } + } + + if (new_status != s->status) { + es1370_update_status (s, new_status); + } +} + +static void es1370_dac1_callback (void *opaque, int free) +{ + ES1370State *s = opaque; + + es1370_run_channel (s, DAC1_CHANNEL, free); +} + +static void es1370_dac2_callback (void *opaque, int free) +{ + ES1370State *s = opaque; + + es1370_run_channel (s, DAC2_CHANNEL, free); +} + +static void es1370_adc_callback (void *opaque, int avail) +{ + ES1370State *s = opaque; + + es1370_run_channel (s, ADC_CHANNEL, avail); +} + +static void es1370_map (PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + PCIES1370State *d = (PCIES1370State *) pci_dev; + ES1370State *s = &d->es1370; + + (void) region_num; + (void) size; + (void) type; + + register_ioport_write (addr, 0x40 * 4, 1, es1370_writeb, s); + register_ioport_write (addr, 0x40 * 2, 2, es1370_writew, s); + register_ioport_write (addr, 0x40, 4, es1370_writel, s); + + register_ioport_read (addr, 0x40 * 4, 1, es1370_readb, s); + register_ioport_read (addr, 0x40 * 2, 2, es1370_readw, s); + register_ioport_read (addr, 0x40, 4, es1370_readl, s); +} + +static void es1370_save (QEMUFile *f, void *opaque) +{ + ES1370State *s = opaque; + size_t i; + + for (i = 0; i < NB_CHANNELS; ++i) { + struct chan *d = &s->chan[i]; + qemu_put_be32s (f, &d->shift); + qemu_put_be32s (f, &d->leftover); + qemu_put_be32s (f, &d->scount); + qemu_put_be32s (f, &d->frame_addr); + qemu_put_be32s (f, &d->frame_cnt); + } + qemu_put_be32s (f, &s->ctl); + qemu_put_be32s (f, &s->status); + qemu_put_be32s (f, &s->mempage); + qemu_put_be32s (f, &s->codec); + qemu_put_be32s (f, &s->sctl); +} + +static int es1370_load (QEMUFile *f, void *opaque, int version_id) +{ + uint32_t ctl, sctl; + ES1370State *s = opaque; + size_t i; + + if (version_id != 1) + return -EINVAL; + + for (i = 0; i < NB_CHANNELS; ++i) { + struct chan *d = &s->chan[i]; + qemu_get_be32s (f, &d->shift); + qemu_get_be32s (f, &d->leftover); + qemu_get_be32s (f, &d->scount); + qemu_get_be32s (f, &d->frame_addr); + qemu_get_be32s (f, &d->frame_cnt); + if (i == ADC_CHANNEL) { + if (s->adc_voice) { + AUD_close_in (&s->card, s->adc_voice); + s->adc_voice = NULL; + } + } + else { + if (s->dac_voice[i]) { + AUD_close_out (&s->card, s->dac_voice[i]); + s->dac_voice[i] = NULL; + } + } + } + + qemu_get_be32s (f, &ctl); + qemu_get_be32s (f, &s->status); + qemu_get_be32s (f, &s->mempage); + qemu_get_be32s (f, &s->codec); + qemu_get_be32s (f, &sctl); + + s->ctl = 0; + s->sctl = 0; + es1370_update_voices (s, ctl, sctl); + return 0; +} + +static void es1370_on_reset (void *opaque) +{ + ES1370State *s = opaque; + es1370_reset (s); +} + +int es1370_init (PCIBus *bus, AudioState *audio) +{ + PCIES1370State *d; + ES1370State *s; + uint8_t *c; + + if (!bus) { + dolog ("No PCI bus\n"); + return -1; + } + + if (!audio) { + dolog ("No audio state\n"); + return -1; + } + + d = (PCIES1370State *) pci_register_device (bus, "ES1370", + sizeof (PCIES1370State), + -1, NULL, NULL); + + if (!d) { + AUD_log (NULL, "Failed to register PCI device for ES1370\n"); + return -1; + } + + c = d->dev.config; + c[0x00] = 0x74; + c[0x01] = 0x12; + c[0x02] = 0x00; + c[0x03] = 0x50; + c[0x07] = 2 << 1; + c[0x0a] = 0x01; + c[0x0b] = 0x04; + +#if 1 + c[0x2c] = 0x42; + c[0x2d] = 0x49; + c[0x2e] = 0x4c; + c[0x2f] = 0x4c; +#else + c[0x2c] = 0x74; + c[0x2d] = 0x12; + c[0x2e] = 0x71; + c[0x2f] = 0x13; + c[0x34] = 0xdc; + c[0x3c] = 10; + c[0xdc] = 0x00; +#endif + + c[0x3d] = 1; + c[0x3e] = 0x0c; + c[0x3f] = 0x80; + + s = &d->es1370; + s->pci_dev = &d->dev; + + pci_register_io_region (&d->dev, 0, 256, PCI_ADDRESS_SPACE_IO, es1370_map); + register_savevm ("es1370", 0, 1, es1370_save, es1370_load, s); + qemu_register_reset (es1370_on_reset, s); + + AUD_register_card (audio, "es1370", &s->card); + es1370_reset (s); + return 0; +} diff --git a/tools/ioemu/hw/esp.c b/tools/ioemu/hw/esp.c new file mode 100644 index 0000000000..c0acbe6783 --- /dev/null +++ b/tools/ioemu/hw/esp.c @@ -0,0 +1,747 @@ +/* + * QEMU ESP emulation + * + * Copyright (c) 2005-2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* debug ESP card */ +//#define DEBUG_ESP + +#ifdef DEBUG_ESP +#define DPRINTF(fmt, args...) \ +do { printf("ESP: " fmt , ##args); } while (0) +#define pic_set_irq(irq, level) \ +do { printf("ESP: set_irq(%d): %d\n", (irq), (level)); pic_set_irq((irq),(level));} while (0) +#else +#define DPRINTF(fmt, args...) +#endif + +#define ESPDMA_REGS 4 +#define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1) +#define ESP_MAXREG 0x3f +#define TI_BUFSZ 1024*1024 // XXX +#define DMA_VER 0xa0000000 +#define DMA_INTR 1 +#define DMA_INTREN 0x10 +#define DMA_LOADED 0x04000000 +typedef struct ESPState ESPState; + +typedef int ESPDMAFunc(ESPState *s, + target_phys_addr_t phys_addr, + int transfer_size1); + +struct ESPState { + BlockDriverState **bd; + uint8_t rregs[ESP_MAXREG]; + uint8_t wregs[ESP_MAXREG]; + int irq; + uint32_t espdmaregs[ESPDMA_REGS]; + uint32_t ti_size; + uint32_t ti_rptr, ti_wptr; + int ti_dir; + uint8_t ti_buf[TI_BUFSZ]; + int dma; + ESPDMAFunc *dma_cb; + int64_t offset, len; + int target; +}; + +#define STAT_DO 0x00 +#define STAT_DI 0x01 +#define STAT_CD 0x02 +#define STAT_ST 0x03 +#define STAT_MI 0x06 +#define STAT_MO 0x07 + +#define STAT_TC 0x10 +#define STAT_IN 0x80 + +#define INTR_FC 0x08 +#define INTR_BS 0x10 +#define INTR_DC 0x20 +#define INTR_RST 0x80 + +#define SEQ_0 0x0 +#define SEQ_CD 0x4 + +/* XXX: stolen from ide.c, move to common ATAPI/SCSI library */ +static void lba_to_msf(uint8_t *buf, int lba) +{ + lba += 150; + buf[0] = (lba / 75) / 60; + buf[1] = (lba / 75) % 60; + buf[2] = lba % 75; +} + +static inline void cpu_to_ube16(uint8_t *buf, int val) +{ + buf[0] = val >> 8; + buf[1] = val; +} + +static inline void cpu_to_ube32(uint8_t *buf, unsigned int val) +{ + buf[0] = val >> 24; + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val; +} + +/* same toc as bochs. Return -1 if error or the toc length */ +/* XXX: check this */ +static int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track) +{ + uint8_t *q; + int len; + + if (start_track > 1 && start_track != 0xaa) + return -1; + q = buf + 2; + *q++ = 1; /* first session */ + *q++ = 1; /* last session */ + if (start_track <= 1) { + *q++ = 0; /* reserved */ + *q++ = 0x14; /* ADR, control */ + *q++ = 1; /* track number */ + *q++ = 0; /* reserved */ + if (msf) { + *q++ = 0; /* reserved */ + lba_to_msf(q, 0); + q += 3; + } else { + /* sector 0 */ + cpu_to_ube32(q, 0); + q += 4; + } + } + /* lead out track */ + *q++ = 0; /* reserved */ + *q++ = 0x16; /* ADR, control */ + *q++ = 0xaa; /* track number */ + *q++ = 0; /* reserved */ + if (msf) { + *q++ = 0; /* reserved */ + lba_to_msf(q, nb_sectors); + q += 3; + } else { + cpu_to_ube32(q, nb_sectors); + q += 4; + } + len = q - buf; + cpu_to_ube16(buf, len - 2); + return len; +} + +/* mostly same info as PearPc */ +static int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, + int session_num) +{ + uint8_t *q; + int len; + + q = buf + 2; + *q++ = 1; /* first session */ + *q++ = 1; /* last session */ + + *q++ = 1; /* session number */ + *q++ = 0x14; /* data track */ + *q++ = 0; /* track number */ + *q++ = 0xa0; /* lead-in */ + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + *q++ = 0; + *q++ = 1; /* first track */ + *q++ = 0x00; /* disk type */ + *q++ = 0x00; + + *q++ = 1; /* session number */ + *q++ = 0x14; /* data track */ + *q++ = 0; /* track number */ + *q++ = 0xa1; + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + *q++ = 0; + *q++ = 1; /* last track */ + *q++ = 0x00; + *q++ = 0x00; + + *q++ = 1; /* session number */ + *q++ = 0x14; /* data track */ + *q++ = 0; /* track number */ + *q++ = 0xa2; /* lead-out */ + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + if (msf) { + *q++ = 0; /* reserved */ + lba_to_msf(q, nb_sectors); + q += 3; + } else { + cpu_to_ube32(q, nb_sectors); + q += 4; + } + + *q++ = 1; /* session number */ + *q++ = 0x14; /* ADR, control */ + *q++ = 0; /* track number */ + *q++ = 1; /* point */ + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + if (msf) { + *q++ = 0; + lba_to_msf(q, 0); + q += 3; + } else { + *q++ = 0; + *q++ = 0; + *q++ = 0; + *q++ = 0; + } + + len = q - buf; + cpu_to_ube16(buf, len - 2); + return len; +} + +static int esp_write_dma_cb(ESPState *s, + target_phys_addr_t phys_addr, + int transfer_size1) +{ + DPRINTF("Write callback (offset %lld len %lld size %d trans_size %d)\n", + s->offset, s->len, s->ti_size, transfer_size1); + bdrv_write(s->bd[s->target], s->offset, s->ti_buf, s->len); + s->offset = 0; + s->len = 0; + s->target = 0; + return 0; +} + +static void handle_satn(ESPState *s) +{ + uint8_t buf[32]; + uint32_t dmaptr, dmalen; + unsigned int i; + int64_t nb_sectors; + int target; + + dmalen = s->wregs[0] | (s->wregs[1] << 8); + target = s->wregs[4] & 7; + DPRINTF("Select with ATN len %d target %d\n", dmalen, target); + if (s->dma) { + dmaptr = iommu_translate(s->espdmaregs[1]); + DPRINTF("DMA Direction: %c, addr 0x%8.8x\n", s->espdmaregs[0] & 0x100? 'w': 'r', dmaptr); + cpu_physical_memory_read(dmaptr, buf, dmalen); + } else { + buf[0] = 0; + memcpy(&buf[1], s->ti_buf, dmalen); + dmalen++; + } + for (i = 0; i < dmalen; i++) { + DPRINTF("Command %2.2x\n", buf[i]); + } + s->ti_dir = 0; + s->ti_size = 0; + s->ti_rptr = 0; + s->ti_wptr = 0; + + if (target >= 4 || !s->bd[target]) { // No such drive + s->rregs[4] = STAT_IN; + s->rregs[5] = INTR_DC; + s->rregs[6] = SEQ_0; + s->espdmaregs[0] |= DMA_INTR; + pic_set_irq(s->irq, 1); + return; + } + switch (buf[1]) { + case 0x0: + DPRINTF("Test Unit Ready (len %d)\n", buf[5]); + break; + case 0x12: + DPRINTF("Inquiry (len %d)\n", buf[5]); + memset(s->ti_buf, 0, 36); + if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) { + s->ti_buf[0] = 5; + memcpy(&s->ti_buf[16], "QEMU CDROM ", 16); + } else { + s->ti_buf[0] = 0; + memcpy(&s->ti_buf[16], "QEMU HARDDISK ", 16); + } + memcpy(&s->ti_buf[8], "QEMU ", 8); + s->ti_buf[2] = 1; + s->ti_buf[3] = 2; + s->ti_buf[4] = 32; + s->ti_dir = 1; + s->ti_size = 36; + break; + case 0x1a: + DPRINTF("Mode Sense(6) (page %d, len %d)\n", buf[3], buf[5]); + break; + case 0x25: + DPRINTF("Read Capacity (len %d)\n", buf[5]); + memset(s->ti_buf, 0, 8); + bdrv_get_geometry(s->bd[target], &nb_sectors); + s->ti_buf[0] = (nb_sectors >> 24) & 0xff; + s->ti_buf[1] = (nb_sectors >> 16) & 0xff; + s->ti_buf[2] = (nb_sectors >> 8) & 0xff; + s->ti_buf[3] = nb_sectors & 0xff; + s->ti_buf[4] = 0; + s->ti_buf[5] = 0; + if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) + s->ti_buf[6] = 8; // sector size 2048 + else + s->ti_buf[6] = 2; // sector size 512 + s->ti_buf[7] = 0; + s->ti_dir = 1; + s->ti_size = 8; + break; + case 0x28: + { + int64_t offset, len; + + if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) { + offset = ((buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]) * 4; + len = ((buf[8] << 8) | buf[9]) * 4; + s->ti_size = len * 2048; + } else { + offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]; + len = (buf[8] << 8) | buf[9]; + s->ti_size = len * 512; + } + DPRINTF("Read (10) (offset %lld len %lld)\n", offset, len); + if (s->ti_size > TI_BUFSZ) { + DPRINTF("size too large %d\n", s->ti_size); + } + bdrv_read(s->bd[target], offset, s->ti_buf, len); + // XXX error handling + s->ti_dir = 1; + break; + } + case 0x2a: + { + int64_t offset, len; + + if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) { + offset = ((buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]) * 4; + len = ((buf[8] << 8) | buf[9]) * 4; + s->ti_size = len * 2048; + } else { + offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]; + len = (buf[8] << 8) | buf[9]; + s->ti_size = len * 512; + } + DPRINTF("Write (10) (offset %lld len %lld)\n", offset, len); + if (s->ti_size > TI_BUFSZ) { + DPRINTF("size too large %d\n", s->ti_size); + } + s->dma_cb = esp_write_dma_cb; + s->offset = offset; + s->len = len; + s->target = target; + // XXX error handling + s->ti_dir = 0; + break; + } + case 0x43: + { + int start_track, format, msf, len; + + msf = buf[2] & 2; + format = buf[3] & 0xf; + start_track = buf[7]; + bdrv_get_geometry(s->bd[target], &nb_sectors); + DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1); + switch(format) { + case 0: + len = cdrom_read_toc(nb_sectors, buf, msf, start_track); + if (len < 0) + goto error_cmd; + s->ti_size = len; + break; + case 1: + /* multi session : only a single session defined */ + memset(buf, 0, 12); + buf[1] = 0x0a; + buf[2] = 0x01; + buf[3] = 0x01; + s->ti_size = 12; + break; + case 2: + len = cdrom_read_toc_raw(nb_sectors, buf, msf, start_track); + if (len < 0) + goto error_cmd; + s->ti_size = len; + break; + default: + error_cmd: + DPRINTF("Read TOC error\n"); + // XXX error handling + break; + } + s->ti_dir = 1; + break; + } + default: + DPRINTF("Unknown SCSI command (%2.2x)\n", buf[1]); + break; + } + s->rregs[4] = STAT_IN | STAT_TC | STAT_DI; + s->rregs[5] = INTR_BS | INTR_FC; + s->rregs[6] = SEQ_CD; + s->espdmaregs[0] |= DMA_INTR; + pic_set_irq(s->irq, 1); +} + +static void dma_write(ESPState *s, const uint8_t *buf, uint32_t len) +{ + uint32_t dmaptr, dmalen; + + dmalen = s->wregs[0] | (s->wregs[1] << 8); + DPRINTF("Transfer status len %d\n", dmalen); + if (s->dma) { + dmaptr = iommu_translate(s->espdmaregs[1]); + DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r'); + cpu_physical_memory_write(dmaptr, buf, len); + s->rregs[4] = STAT_IN | STAT_TC | STAT_ST; + s->rregs[5] = INTR_BS | INTR_FC; + s->rregs[6] = SEQ_CD; + } else { + memcpy(s->ti_buf, buf, len); + s->ti_size = dmalen; + s->ti_rptr = 0; + s->ti_wptr = 0; + s->rregs[7] = dmalen; + } + s->espdmaregs[0] |= DMA_INTR; + pic_set_irq(s->irq, 1); + +} + +static const uint8_t okbuf[] = {0, 0}; + +static void handle_ti(ESPState *s) +{ + uint32_t dmaptr, dmalen; + unsigned int i; + + dmalen = s->wregs[0] | (s->wregs[1] << 8); + DPRINTF("Transfer Information len %d\n", dmalen); + if (s->dma) { + dmaptr = iommu_translate(s->espdmaregs[1]); + DPRINTF("DMA Direction: %c, addr 0x%8.8x\n", s->espdmaregs[0] & 0x100? 'w': 'r', dmaptr); + for (i = 0; i < s->ti_size; i++) { + dmaptr = iommu_translate(s->espdmaregs[1] + i); + if (s->ti_dir) + cpu_physical_memory_write(dmaptr, &s->ti_buf[i], 1); + else + cpu_physical_memory_read(dmaptr, &s->ti_buf[i], 1); + } + if (s->dma_cb) { + s->dma_cb(s, s->espdmaregs[1], dmalen); + s->dma_cb = NULL; + } + s->rregs[4] = STAT_IN | STAT_TC | STAT_ST; + s->rregs[5] = INTR_BS; + s->rregs[6] = 0; + s->espdmaregs[0] |= DMA_INTR; + } else { + s->ti_size = dmalen; + s->ti_rptr = 0; + s->ti_wptr = 0; + s->rregs[7] = dmalen; + } + pic_set_irq(s->irq, 1); +} + +static void esp_reset(void *opaque) +{ + ESPState *s = opaque; + memset(s->rregs, 0, ESP_MAXREG); + memset(s->wregs, 0, ESP_MAXREG); + s->rregs[0x0e] = 0x4; // Indicate fas100a + memset(s->espdmaregs, 0, ESPDMA_REGS * 4); + s->ti_size = 0; + s->ti_rptr = 0; + s->ti_wptr = 0; + s->ti_dir = 0; + s->dma = 0; + s->dma_cb = NULL; +} + +static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr) +{ + ESPState *s = opaque; + uint32_t saddr; + + saddr = (addr & ESP_MAXREG) >> 2; + DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]); + switch (saddr) { + case 2: + // FIFO + if (s->ti_size > 0) { + s->ti_size--; + s->rregs[saddr] = s->ti_buf[s->ti_rptr++]; + pic_set_irq(s->irq, 1); + } + if (s->ti_size == 0) { + s->ti_rptr = 0; + s->ti_wptr = 0; + } + break; + case 5: + // interrupt + // Clear status bits except TC + s->rregs[4] &= STAT_TC; + pic_set_irq(s->irq, 0); + s->espdmaregs[0] &= ~DMA_INTR; + break; + default: + break; + } + return s->rregs[saddr]; +} + +static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + ESPState *s = opaque; + uint32_t saddr; + + saddr = (addr & ESP_MAXREG) >> 2; + DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr], val); + switch (saddr) { + case 0: + case 1: + s->rregs[saddr] = val; + break; + case 2: + // FIFO + s->ti_size++; + s->ti_buf[s->ti_wptr++] = val & 0xff; + break; + case 3: + s->rregs[saddr] = val; + // Command + if (val & 0x80) { + s->dma = 1; + } else { + s->dma = 0; + } + switch(val & 0x7f) { + case 0: + DPRINTF("NOP (%2.2x)\n", val); + break; + case 1: + DPRINTF("Flush FIFO (%2.2x)\n", val); + //s->ti_size = 0; + s->rregs[5] = INTR_FC; + s->rregs[6] = 0; + break; + case 2: + DPRINTF("Chip reset (%2.2x)\n", val); + esp_reset(s); + break; + case 3: + DPRINTF("Bus reset (%2.2x)\n", val); + s->rregs[5] = INTR_RST; + if (!(s->wregs[8] & 0x40)) { + s->espdmaregs[0] |= DMA_INTR; + pic_set_irq(s->irq, 1); + } + break; + case 0x10: + handle_ti(s); + break; + case 0x11: + DPRINTF("Initiator Command Complete Sequence (%2.2x)\n", val); + dma_write(s, okbuf, 2); + break; + case 0x12: + DPRINTF("Message Accepted (%2.2x)\n", val); + dma_write(s, okbuf, 2); + s->rregs[5] = INTR_DC; + s->rregs[6] = 0; + break; + case 0x1a: + DPRINTF("Set ATN (%2.2x)\n", val); + break; + case 0x42: + handle_satn(s); + break; + case 0x43: + DPRINTF("Set ATN & stop (%2.2x)\n", val); + handle_satn(s); + break; + default: + DPRINTF("Unhandled ESP command (%2.2x)\n", val); + break; + } + break; + case 4 ... 7: + break; + case 8: + s->rregs[saddr] = val; + break; + case 9 ... 10: + break; + case 11: + s->rregs[saddr] = val & 0x15; + break; + case 12 ... 15: + s->rregs[saddr] = val; + break; + default: + break; + } + s->wregs[saddr] = val; +} + +static CPUReadMemoryFunc *esp_mem_read[3] = { + esp_mem_readb, + esp_mem_readb, + esp_mem_readb, +}; + +static CPUWriteMemoryFunc *esp_mem_write[3] = { + esp_mem_writeb, + esp_mem_writeb, + esp_mem_writeb, +}; + +static uint32_t espdma_mem_readl(void *opaque, target_phys_addr_t addr) +{ + ESPState *s = opaque; + uint32_t saddr; + + saddr = (addr & ESPDMA_MAXADDR) >> 2; + DPRINTF("read dmareg[%d]: 0x%8.8x\n", saddr, s->espdmaregs[saddr]); + + return s->espdmaregs[saddr]; +} + +static void espdma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + ESPState *s = opaque; + uint32_t saddr; + + saddr = (addr & ESPDMA_MAXADDR) >> 2; + DPRINTF("write dmareg[%d]: 0x%8.8x -> 0x%8.8x\n", saddr, s->espdmaregs[saddr], val); + switch (saddr) { + case 0: + if (!(val & DMA_INTREN)) + pic_set_irq(s->irq, 0); + if (val & 0x80) { + esp_reset(s); + } else if (val & 0x40) { + val &= ~0x40; + } else if (val == 0) + val = 0x40; + val &= 0x0fffffff; + val |= DMA_VER; + break; + case 1: + s->espdmaregs[0] = DMA_LOADED; + break; + default: + break; + } + s->espdmaregs[saddr] = val; +} + +static CPUReadMemoryFunc *espdma_mem_read[3] = { + espdma_mem_readl, + espdma_mem_readl, + espdma_mem_readl, +}; + +static CPUWriteMemoryFunc *espdma_mem_write[3] = { + espdma_mem_writel, + espdma_mem_writel, + espdma_mem_writel, +}; + +static void esp_save(QEMUFile *f, void *opaque) +{ + ESPState *s = opaque; + unsigned int i; + + qemu_put_buffer(f, s->rregs, ESP_MAXREG); + qemu_put_buffer(f, s->wregs, ESP_MAXREG); + qemu_put_be32s(f, &s->irq); + for (i = 0; i < ESPDMA_REGS; i++) + qemu_put_be32s(f, &s->espdmaregs[i]); + qemu_put_be32s(f, &s->ti_size); + qemu_put_be32s(f, &s->ti_rptr); + qemu_put_be32s(f, &s->ti_wptr); + qemu_put_be32s(f, &s->ti_dir); + qemu_put_buffer(f, s->ti_buf, TI_BUFSZ); + qemu_put_be32s(f, &s->dma); +} + +static int esp_load(QEMUFile *f, void *opaque, int version_id) +{ + ESPState *s = opaque; + unsigned int i; + + if (version_id != 1) + return -EINVAL; + + qemu_get_buffer(f, s->rregs, ESP_MAXREG); + qemu_get_buffer(f, s->wregs, ESP_MAXREG); + qemu_get_be32s(f, &s->irq); + for (i = 0; i < ESPDMA_REGS; i++) + qemu_get_be32s(f, &s->espdmaregs[i]); + qemu_get_be32s(f, &s->ti_size); + qemu_get_be32s(f, &s->ti_rptr); + qemu_get_be32s(f, &s->ti_wptr); + qemu_get_be32s(f, &s->ti_dir); + qemu_get_buffer(f, s->ti_buf, TI_BUFSZ); + qemu_get_be32s(f, &s->dma); + + return 0; +} + +void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdaddr) +{ + ESPState *s; + int esp_io_memory, espdma_io_memory; + + s = qemu_mallocz(sizeof(ESPState)); + if (!s) + return; + + s->bd = bd; + s->irq = irq; + + esp_io_memory = cpu_register_io_memory(0, esp_mem_read, esp_mem_write, s); + cpu_register_physical_memory(espaddr, ESP_MAXREG*4, esp_io_memory); + + espdma_io_memory = cpu_register_io_memory(0, espdma_mem_read, espdma_mem_write, s); + cpu_register_physical_memory(espdaddr, 16, espdma_io_memory); + + esp_reset(s); + + register_savevm("esp", espaddr, 1, esp_save, esp_load, s); + qemu_register_reset(esp_reset, s); +} + diff --git a/tools/ioemu/hw/fdc.c b/tools/ioemu/hw/fdc.c new file mode 100644 index 0000000000..3890ace120 --- /dev/null +++ b/tools/ioemu/hw/fdc.c @@ -0,0 +1,1757 @@ +/* + * QEMU Floppy disk emulator (Intel 82078) + * + * Copyright (c) 2003 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* + * The controller is used in Sun4m systems in a slightly different + * way. There are changes in DOR register and DMA is not available. + */ +#include "vl.h" + +/********************************************************/ +/* debug Floppy devices */ +//#define DEBUG_FLOPPY + +#ifdef DEBUG_FLOPPY +#define FLOPPY_DPRINTF(fmt, args...) \ +do { printf("FLOPPY: " fmt , ##args); } while (0) +#else +#define FLOPPY_DPRINTF(fmt, args...) +#endif + +#define FLOPPY_ERROR(fmt, args...) \ +do { printf("FLOPPY ERROR: %s: " fmt, __func__ , ##args); } while (0) + +/********************************************************/ +/* Floppy drive emulation */ + +/* Will always be a fixed parameter for us */ +#define FD_SECTOR_LEN 512 +#define FD_SECTOR_SC 2 /* Sector size code */ + +/* Floppy disk drive emulation */ +typedef enum fdisk_type_t { + FDRIVE_DISK_288 = 0x01, /* 2.88 MB disk */ + FDRIVE_DISK_144 = 0x02, /* 1.44 MB disk */ + FDRIVE_DISK_720 = 0x03, /* 720 kB disk */ + FDRIVE_DISK_USER = 0x04, /* User defined geometry */ + FDRIVE_DISK_NONE = 0x05, /* No disk */ +} fdisk_type_t; + +typedef enum fdrive_type_t { + FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */ + FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */ + FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */ + FDRIVE_DRV_NONE = 0x03, /* No drive connected */ +} fdrive_type_t; + +typedef enum fdrive_flags_t { + FDRIVE_MOTOR_ON = 0x01, /* motor on/off */ + FDRIVE_REVALIDATE = 0x02, /* Revalidated */ +} fdrive_flags_t; + +typedef enum fdisk_flags_t { + FDISK_DBL_SIDES = 0x01, +} fdisk_flags_t; + +typedef struct fdrive_t { + BlockDriverState *bs; + /* Drive status */ + fdrive_type_t drive; + fdrive_flags_t drflags; + uint8_t perpendicular; /* 2.88 MB access mode */ + /* Position */ + uint8_t head; + uint8_t track; + uint8_t sect; + /* Last operation status */ + uint8_t dir; /* Direction */ + uint8_t rw; /* Read/write */ + /* Media */ + fdisk_flags_t flags; + uint8_t last_sect; /* Nb sector per track */ + uint8_t max_track; /* Nb of tracks */ + uint16_t bps; /* Bytes per sector */ + uint8_t ro; /* Is read-only */ +} fdrive_t; + +static void fd_init (fdrive_t *drv, BlockDriverState *bs) +{ + /* Drive */ + drv->bs = bs; + drv->drive = FDRIVE_DRV_NONE; + drv->drflags = 0; + drv->perpendicular = 0; + /* Disk */ + drv->last_sect = 0; + drv->max_track = 0; +} + +static int _fd_sector (uint8_t head, uint8_t track, + uint8_t sect, uint8_t last_sect) +{ + return (((track * 2) + head) * last_sect) + sect - 1; +} + +/* Returns current position, in sectors, for given drive */ +static int fd_sector (fdrive_t *drv) +{ + return _fd_sector(drv->head, drv->track, drv->sect, drv->last_sect); +} + +static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect, + int enable_seek) +{ + uint32_t sector; + int ret; + + if (track > drv->max_track || + (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) { + FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", + head, track, sect, 1, + (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, + drv->max_track, drv->last_sect); + return 2; + } + if (sect > drv->last_sect) { + FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", + head, track, sect, 1, + (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, + drv->max_track, drv->last_sect); + return 3; + } + sector = _fd_sector(head, track, sect, drv->last_sect); + ret = 0; + if (sector != fd_sector(drv)) { +#if 0 + if (!enable_seek) { + FLOPPY_ERROR("no implicit seek %d %02x %02x (max=%d %02x %02x)\n", + head, track, sect, 1, drv->max_track, drv->last_sect); + return 4; + } +#endif + drv->head = head; + if (drv->track != track) + ret = 1; + drv->track = track; + drv->sect = sect; + } + + return ret; +} + +/* Set drive back to track 0 */ +static void fd_recalibrate (fdrive_t *drv) +{ + FLOPPY_DPRINTF("recalibrate\n"); + drv->head = 0; + drv->track = 0; + drv->sect = 1; + drv->dir = 1; + drv->rw = 0; +} + +/* Recognize floppy formats */ +typedef struct fd_format_t { + fdrive_type_t drive; + fdisk_type_t disk; + uint8_t last_sect; + uint8_t max_track; + uint8_t max_head; + const unsigned char *str; +} fd_format_t; + +static fd_format_t fd_formats[] = { + /* First entry is default format */ + /* 1.44 MB 3"1/2 floppy disks */ + { FDRIVE_DRV_144, FDRIVE_DISK_144, 18, 80, 1, "1.44 MB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_144, 20, 80, 1, "1.6 MB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 80, 1, "1.68 MB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 82, 1, "1.72 MB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 83, 1, "1.74 MB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_144, 22, 80, 1, "1.76 MB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_144, 23, 80, 1, "1.84 MB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_144, 24, 80, 1, "1.92 MB 3\"1/2", }, + /* 2.88 MB 3"1/2 floppy disks */ + { FDRIVE_DRV_288, FDRIVE_DISK_288, 36, 80, 1, "2.88 MB 3\"1/2", }, + { FDRIVE_DRV_288, FDRIVE_DISK_288, 39, 80, 1, "3.12 MB 3\"1/2", }, + { FDRIVE_DRV_288, FDRIVE_DISK_288, 40, 80, 1, "3.2 MB 3\"1/2", }, + { FDRIVE_DRV_288, FDRIVE_DISK_288, 44, 80, 1, "3.52 MB 3\"1/2", }, + { FDRIVE_DRV_288, FDRIVE_DISK_288, 48, 80, 1, "3.84 MB 3\"1/2", }, + /* 720 kB 3"1/2 floppy disks */ + { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 1, "720 kB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 80, 1, "800 kB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 82, 1, "820 kB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 83, 1, "830 kB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_720, 13, 80, 1, "1.04 MB 3\"1/2", }, + { FDRIVE_DRV_144, FDRIVE_DISK_720, 14, 80, 1, "1.12 MB 3\"1/2", }, + /* 1.2 MB 5"1/4 floppy disks */ + { FDRIVE_DRV_120, FDRIVE_DISK_288, 15, 80, 1, "1.2 kB 5\"1/4", }, + { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 80, 1, "1.44 MB 5\"1/4", }, + { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 82, 1, "1.48 MB 5\"1/4", }, + { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 83, 1, "1.49 MB 5\"1/4", }, + { FDRIVE_DRV_120, FDRIVE_DISK_288, 20, 80, 1, "1.6 MB 5\"1/4", }, + /* 720 kB 5"1/4 floppy disks */ + { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 80, 1, "720 kB 5\"1/4", }, + { FDRIVE_DRV_120, FDRIVE_DISK_288, 11, 80, 1, "880 kB 5\"1/4", }, + /* 360 kB 5"1/4 floppy disks */ + { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 1, "360 kB 5\"1/4", }, + { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, "180 kB 5\"1/4", }, + { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, "410 kB 5\"1/4", }, + { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, "420 kB 5\"1/4", }, + /* 320 kB 5"1/4 floppy disks */ + { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, "320 kB 5\"1/4", }, + { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, "160 kB 5\"1/4", }, + /* 360 kB must match 5"1/4 better than 3"1/2... */ + { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 0, "360 kB 3\"1/2", }, + /* end */ + { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, NULL, }, +}; + +/* Revalidate a disk drive after a disk change */ +static void fd_revalidate (fdrive_t *drv) +{ + fd_format_t *parse; + int64_t nb_sectors, size; + int i, first_match, match; + int nb_heads, max_track, last_sect, ro; + + FLOPPY_DPRINTF("revalidate\n"); + drv->drflags &= ~FDRIVE_REVALIDATE; + if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) { + ro = bdrv_is_read_only(drv->bs); + bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect); + if (nb_heads != 0 && max_track != 0 && last_sect != 0) { + FLOPPY_DPRINTF("User defined disk (%d %d %d)", + nb_heads - 1, max_track, last_sect); + } else { + bdrv_get_geometry(drv->bs, &nb_sectors); + match = -1; + first_match = -1; + for (i = 0;; i++) { + parse = &fd_formats[i]; + if (parse->drive == FDRIVE_DRV_NONE) + break; + if (drv->drive == parse->drive || + drv->drive == FDRIVE_DRV_NONE) { + size = (parse->max_head + 1) * parse->max_track * + parse->last_sect; + if (nb_sectors == size) { + match = i; + break; + } + if (first_match == -1) + first_match = i; + } + } + if (match == -1) { + if (first_match == -1) + match = 1; + else + match = first_match; + parse = &fd_formats[match]; + } + nb_heads = parse->max_head + 1; + max_track = parse->max_track; + last_sect = parse->last_sect; + drv->drive = parse->drive; + FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str, + nb_heads, max_track, last_sect, ro ? "ro" : "rw"); + } + if (nb_heads == 1) { + drv->flags &= ~FDISK_DBL_SIDES; + } else { + drv->flags |= FDISK_DBL_SIDES; + } + drv->max_track = max_track; + drv->last_sect = last_sect; + drv->ro = ro; + } else { + FLOPPY_DPRINTF("No disk in drive\n"); + drv->last_sect = 0; + drv->max_track = 0; + drv->flags &= ~FDISK_DBL_SIDES; + } + drv->drflags |= FDRIVE_REVALIDATE; +} + +/* Motor control */ +static void fd_start (fdrive_t *drv) +{ + drv->drflags |= FDRIVE_MOTOR_ON; +} + +static void fd_stop (fdrive_t *drv) +{ + drv->drflags &= ~FDRIVE_MOTOR_ON; +} + +/* Re-initialise a drives (motor off, repositioned) */ +static void fd_reset (fdrive_t *drv) +{ + fd_stop(drv); + fd_recalibrate(drv); +} + +/********************************************************/ +/* Intel 82078 floppy disk controller emulation */ + +static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq); +static void fdctrl_reset_fifo (fdctrl_t *fdctrl); +static int fdctrl_transfer_handler (void *opaque, int nchan, + int dma_pos, int dma_len); +static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status); +static void fdctrl_result_timer(void *opaque); + +static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl); +static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl); +static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value); +static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl); +static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value); +static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl); +static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value); +static uint32_t fdctrl_read_data (fdctrl_t *fdctrl); +static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value); +static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl); + +enum { + FD_CTRL_ACTIVE = 0x01, /* XXX: suppress that */ + FD_CTRL_RESET = 0x02, + FD_CTRL_SLEEP = 0x04, /* XXX: suppress that */ + FD_CTRL_BUSY = 0x08, /* dma transfer in progress */ + FD_CTRL_INTR = 0x10, +}; + +enum { + FD_DIR_WRITE = 0, + FD_DIR_READ = 1, + FD_DIR_SCANE = 2, + FD_DIR_SCANL = 3, + FD_DIR_SCANH = 4, +}; + +enum { + FD_STATE_CMD = 0x00, + FD_STATE_STATUS = 0x01, + FD_STATE_DATA = 0x02, + FD_STATE_STATE = 0x03, + FD_STATE_MULTI = 0x10, + FD_STATE_SEEK = 0x20, + FD_STATE_FORMAT = 0x40, +}; + +#define FD_STATE(state) ((state) & FD_STATE_STATE) +#define FD_SET_STATE(state, new_state) \ +do { (state) = ((state) & ~FD_STATE_STATE) | (new_state); } while (0) +#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI) +#define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK) +#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT) + +struct fdctrl_t { + fdctrl_t *fdctrl; + /* Controller's identification */ + uint8_t version; + /* HW */ + int irq_lvl; + int dma_chann; + uint32_t io_base; + /* Controller state */ + QEMUTimer *result_timer; + uint8_t state; + uint8_t dma_en; + uint8_t cur_drv; + uint8_t bootsel; + /* Command FIFO */ + uint8_t fifo[FD_SECTOR_LEN]; + uint32_t data_pos; + uint32_t data_len; + uint8_t data_state; + uint8_t data_dir; + uint8_t int_status; + uint8_t eot; /* last wanted sector */ + /* States kept only to be returned back */ + /* Timers state */ + uint8_t timer0; + uint8_t timer1; + /* precompensation */ + uint8_t precomp_trk; + uint8_t config; + uint8_t lock; + /* Power down config (also with status regB access mode */ + uint8_t pwrd; + /* Floppy drives */ + fdrive_t drives[2]; +}; + +static uint32_t fdctrl_read (void *opaque, uint32_t reg) +{ + fdctrl_t *fdctrl = opaque; + uint32_t retval; + + switch (reg & 0x07) { +#ifdef TARGET_SPARC + case 0x00: + // Identify to Linux as S82078B + retval = fdctrl_read_statusB(fdctrl); + break; +#endif + case 0x01: + retval = fdctrl_read_statusB(fdctrl); + break; + case 0x02: + retval = fdctrl_read_dor(fdctrl); + break; + case 0x03: + retval = fdctrl_read_tape(fdctrl); + break; + case 0x04: + retval = fdctrl_read_main_status(fdctrl); + break; + case 0x05: + retval = fdctrl_read_data(fdctrl); + break; + case 0x07: + retval = fdctrl_read_dir(fdctrl); + break; + default: + retval = (uint32_t)(-1); + break; + } + FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval); + + return retval; +} + +static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) +{ + fdctrl_t *fdctrl = opaque; + + FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); + + switch (reg & 0x07) { + case 0x02: + fdctrl_write_dor(fdctrl, value); + break; + case 0x03: + fdctrl_write_tape(fdctrl, value); + break; + case 0x04: + fdctrl_write_rate(fdctrl, value); + break; + case 0x05: + fdctrl_write_data(fdctrl, value); + break; + default: + break; + } +} + +static uint32_t fdctrl_read_mem (void *opaque, target_phys_addr_t reg) +{ + return fdctrl_read(opaque, reg); +} + +static void fdctrl_write_mem (void *opaque, + target_phys_addr_t reg, uint32_t value) +{ + fdctrl_write(opaque, reg, value); +} + +static CPUReadMemoryFunc *fdctrl_mem_read[3] = { + fdctrl_read_mem, + fdctrl_read_mem, + fdctrl_read_mem, +}; + +static CPUWriteMemoryFunc *fdctrl_mem_write[3] = { + fdctrl_write_mem, + fdctrl_write_mem, + fdctrl_write_mem, +}; + +static void fd_change_cb (void *opaque) +{ + fdrive_t *drv = opaque; + + FLOPPY_DPRINTF("disk change\n"); + fd_revalidate(drv); +#if 0 + fd_recalibrate(drv); + fdctrl_reset_fifo(drv->fdctrl); + fdctrl_raise_irq(drv->fdctrl, 0x20); +#endif +} + +fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped, + uint32_t io_base, + BlockDriverState **fds) +{ + fdctrl_t *fdctrl; + int io_mem; + int i; + + FLOPPY_DPRINTF("init controller\n"); + fdctrl = qemu_mallocz(sizeof(fdctrl_t)); + if (!fdctrl) + return NULL; + fdctrl->result_timer = qemu_new_timer(vm_clock, + fdctrl_result_timer, fdctrl); + + fdctrl->version = 0x90; /* Intel 82078 controller */ + fdctrl->irq_lvl = irq_lvl; + fdctrl->dma_chann = dma_chann; + fdctrl->io_base = io_base; + fdctrl->config = 0x60; /* Implicit seek, polling & FIFO enabled */ + if (fdctrl->dma_chann != -1) { + fdctrl->dma_en = 1; + DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl); + } else { + fdctrl->dma_en = 0; + } + for (i = 0; i < 2; i++) { + fd_init(&fdctrl->drives[i], fds[i]); + if (fds[i]) { + bdrv_set_change_cb(fds[i], + &fd_change_cb, &fdctrl->drives[i]); + } + } + fdctrl_reset(fdctrl, 0); + fdctrl->state = FD_CTRL_ACTIVE; + if (mem_mapped) { + io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write, fdctrl); + cpu_register_physical_memory(io_base, 0x08, io_mem); + } else { + register_ioport_read(io_base + 0x01, 5, 1, &fdctrl_read, fdctrl); + register_ioport_read(io_base + 0x07, 1, 1, &fdctrl_read, fdctrl); + register_ioport_write(io_base + 0x01, 5, 1, &fdctrl_write, fdctrl); + register_ioport_write(io_base + 0x07, 1, 1, &fdctrl_write, fdctrl); + } + for (i = 0; i < 2; i++) { + fd_revalidate(&fdctrl->drives[i]); + } + + return fdctrl; +} + +/* XXX: may change if moved to bdrv */ +int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num) +{ + return fdctrl->drives[drive_num].drive; +} + +/* Change IRQ state */ +static void fdctrl_reset_irq (fdctrl_t *fdctrl) +{ + FLOPPY_DPRINTF("Reset interrupt\n"); + pic_set_irq(fdctrl->irq_lvl, 0); + fdctrl->state &= ~FD_CTRL_INTR; +} + +static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status) +{ +#ifdef TARGET_SPARC + // Sparc mutation + if (!fdctrl->dma_en) { + fdctrl->state &= ~FD_CTRL_BUSY; + fdctrl->int_status = status; + return; + } +#endif + if (~(fdctrl->state & FD_CTRL_INTR)) { + pic_set_irq(fdctrl->irq_lvl, 1); + fdctrl->state |= FD_CTRL_INTR; + } + FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", status); + fdctrl->int_status = status; +} + +/* Reset controller */ +static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq) +{ + int i; + + FLOPPY_DPRINTF("reset controller\n"); + fdctrl_reset_irq(fdctrl); + /* Initialise controller */ + fdctrl->cur_drv = 0; + /* FIFO state */ + fdctrl->data_pos = 0; + fdctrl->data_len = 0; + fdctrl->data_state = FD_STATE_CMD; + fdctrl->data_dir = FD_DIR_WRITE; + for (i = 0; i < MAX_FD; i++) + fd_reset(&fdctrl->drives[i]); + fdctrl_reset_fifo(fdctrl); + if (do_irq) + fdctrl_raise_irq(fdctrl, 0xc0); +} + +static inline fdrive_t *drv0 (fdctrl_t *fdctrl) +{ + return &fdctrl->drives[fdctrl->bootsel]; +} + +static inline fdrive_t *drv1 (fdctrl_t *fdctrl) +{ + return &fdctrl->drives[1 - fdctrl->bootsel]; +} + +static fdrive_t *get_cur_drv (fdctrl_t *fdctrl) +{ + return fdctrl->cur_drv == 0 ? drv0(fdctrl) : drv1(fdctrl); +} + +/* Status B register : 0x01 (read-only) */ +static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl) +{ + FLOPPY_DPRINTF("status register: 0x00\n"); + return 0; +} + +/* Digital output register : 0x02 */ +static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl) +{ + uint32_t retval = 0; + + /* Drive motors state indicators */ + if (drv0(fdctrl)->drflags & FDRIVE_MOTOR_ON) + retval |= 1 << 5; + if (drv1(fdctrl)->drflags & FDRIVE_MOTOR_ON) + retval |= 1 << 4; + /* DMA enable */ + retval |= fdctrl->dma_en << 3; + /* Reset indicator */ + retval |= (fdctrl->state & FD_CTRL_RESET) == 0 ? 0x04 : 0; + /* Selected drive */ + retval |= fdctrl->cur_drv; + FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval); + + return retval; +} + +static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value) +{ + /* Reset mode */ + if (fdctrl->state & FD_CTRL_RESET) { + if (!(value & 0x04)) { + FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); + return; + } + } + FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value); + /* Drive motors state indicators */ + if (value & 0x20) + fd_start(drv1(fdctrl)); + else + fd_stop(drv1(fdctrl)); + if (value & 0x10) + fd_start(drv0(fdctrl)); + else + fd_stop(drv0(fdctrl)); + /* DMA enable */ +#if 0 + if (fdctrl->dma_chann != -1) + fdctrl->dma_en = 1 - ((value >> 3) & 1); +#endif + /* Reset */ + if (!(value & 0x04)) { + if (!(fdctrl->state & FD_CTRL_RESET)) { + FLOPPY_DPRINTF("controller enter RESET state\n"); + fdctrl->state |= FD_CTRL_RESET; + } + } else { + if (fdctrl->state & FD_CTRL_RESET) { + FLOPPY_DPRINTF("controller out of RESET state\n"); + fdctrl_reset(fdctrl, 1); + fdctrl->state &= ~(FD_CTRL_RESET | FD_CTRL_SLEEP); + } + } + /* Selected drive */ + fdctrl->cur_drv = value & 1; +} + +/* Tape drive register : 0x03 */ +static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl) +{ + uint32_t retval = 0; + + /* Disk boot selection indicator */ + retval |= fdctrl->bootsel << 2; + /* Tape indicators: never allowed */ + FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval); + + return retval; +} + +static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value) +{ + /* Reset mode */ + if (fdctrl->state & FD_CTRL_RESET) { + FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); + return; + } + FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value); + /* Disk boot selection indicator */ + fdctrl->bootsel = (value >> 2) & 1; + /* Tape indicators: never allow */ +} + +/* Main status register : 0x04 (read) */ +static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl) +{ + uint32_t retval = 0; + + fdctrl->state &= ~(FD_CTRL_SLEEP | FD_CTRL_RESET); + if (!(fdctrl->state & FD_CTRL_BUSY)) { + /* Data transfer allowed */ + retval |= 0x80; + /* Data transfer direction indicator */ + if (fdctrl->data_dir == FD_DIR_READ) + retval |= 0x40; + } + /* Should handle 0x20 for SPECIFY command */ + /* Command busy indicator */ + if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA || + FD_STATE(fdctrl->data_state) == FD_STATE_STATUS) + retval |= 0x10; + FLOPPY_DPRINTF("main status register: 0x%02x\n", retval); + + return retval; +} + +/* Data select rate register : 0x04 (write) */ +static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value) +{ + /* Reset mode */ + if (fdctrl->state & FD_CTRL_RESET) { + FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); + return; + } + FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value); + /* Reset: autoclear */ + if (value & 0x80) { + fdctrl->state |= FD_CTRL_RESET; + fdctrl_reset(fdctrl, 1); + fdctrl->state &= ~FD_CTRL_RESET; + } + if (value & 0x40) { + fdctrl->state |= FD_CTRL_SLEEP; + fdctrl_reset(fdctrl, 1); + } +// fdctrl.precomp = (value >> 2) & 0x07; +} + +/* Digital input register : 0x07 (read-only) */ +static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl) +{ + uint32_t retval = 0; + + if (drv0(fdctrl)->drflags & FDRIVE_REVALIDATE || + drv1(fdctrl)->drflags & FDRIVE_REVALIDATE) + retval |= 0x80; + if (retval != 0) + FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval); + drv0(fdctrl)->drflags &= ~FDRIVE_REVALIDATE; + drv1(fdctrl)->drflags &= ~FDRIVE_REVALIDATE; + + return retval; +} + +/* FIFO state control */ +static void fdctrl_reset_fifo (fdctrl_t *fdctrl) +{ + fdctrl->data_dir = FD_DIR_WRITE; + fdctrl->data_pos = 0; + FD_SET_STATE(fdctrl->data_state, FD_STATE_CMD); +} + +/* Set FIFO status for the host to read */ +static void fdctrl_set_fifo (fdctrl_t *fdctrl, int fifo_len, int do_irq) +{ + fdctrl->data_dir = FD_DIR_READ; + fdctrl->data_len = fifo_len; + fdctrl->data_pos = 0; + FD_SET_STATE(fdctrl->data_state, FD_STATE_STATUS); + if (do_irq) + fdctrl_raise_irq(fdctrl, 0x00); +} + +/* Set an error: unimplemented/unknown command */ +static void fdctrl_unimplemented (fdctrl_t *fdctrl) +{ +#if 0 + fdrive_t *cur_drv; + + cur_drv = get_cur_drv(fdctrl); + fdctrl->fifo[0] = 0x60 | (cur_drv->head << 2) | fdctrl->cur_drv; + fdctrl->fifo[1] = 0x00; + fdctrl->fifo[2] = 0x00; + fdctrl_set_fifo(fdctrl, 3, 1); +#else + // fdctrl_reset_fifo(fdctrl); + fdctrl->fifo[0] = 0x80; + fdctrl_set_fifo(fdctrl, 1, 0); +#endif +} + +/* Callback for transfer end (stop or abort) */ +static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0, + uint8_t status1, uint8_t status2) +{ + fdrive_t *cur_drv; + + cur_drv = get_cur_drv(fdctrl); + FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n", + status0, status1, status2, + status0 | (cur_drv->head << 2) | fdctrl->cur_drv); + fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | fdctrl->cur_drv; + fdctrl->fifo[1] = status1; + fdctrl->fifo[2] = status2; + fdctrl->fifo[3] = cur_drv->track; + fdctrl->fifo[4] = cur_drv->head; + fdctrl->fifo[5] = cur_drv->sect; + fdctrl->fifo[6] = FD_SECTOR_SC; + fdctrl->data_dir = FD_DIR_READ; + if (fdctrl->state & FD_CTRL_BUSY) { + DMA_release_DREQ(fdctrl->dma_chann); + fdctrl->state &= ~FD_CTRL_BUSY; + } + fdctrl_set_fifo(fdctrl, 7, 1); +} + +/* Prepare a data transfer (either DMA or FIFO) */ +static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction) +{ + fdrive_t *cur_drv; + uint8_t kh, kt, ks; + int did_seek; + + fdctrl->cur_drv = fdctrl->fifo[1] & 1; + cur_drv = get_cur_drv(fdctrl); + kt = fdctrl->fifo[2]; + kh = fdctrl->fifo[3]; + ks = fdctrl->fifo[4]; + FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n", + fdctrl->cur_drv, kh, kt, ks, + _fd_sector(kh, kt, ks, cur_drv->last_sect)); + did_seek = 0; + switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) { + case 2: + /* sect too big */ + fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; + case 3: + /* track too big */ + fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; + case 4: + /* No seek enabled */ + fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; + case 1: + did_seek = 1; + break; + default: + break; + } + /* Set the FIFO state */ + fdctrl->data_dir = direction; + fdctrl->data_pos = 0; + FD_SET_STATE(fdctrl->data_state, FD_STATE_DATA); /* FIFO ready for data */ + if (fdctrl->fifo[0] & 0x80) + fdctrl->data_state |= FD_STATE_MULTI; + else + fdctrl->data_state &= ~FD_STATE_MULTI; + if (did_seek) + fdctrl->data_state |= FD_STATE_SEEK; + else + fdctrl->data_state &= ~FD_STATE_SEEK; + if (fdctrl->fifo[5] == 00) { + fdctrl->data_len = fdctrl->fifo[8]; + } else { + int tmp; + fdctrl->data_len = 128 << fdctrl->fifo[5]; + tmp = (cur_drv->last_sect - ks + 1); + if (fdctrl->fifo[0] & 0x80) + tmp += cur_drv->last_sect; + fdctrl->data_len *= tmp; + } + fdctrl->eot = fdctrl->fifo[6]; + if (fdctrl->dma_en) { + int dma_mode; + /* DMA transfer are enabled. Check if DMA channel is well programmed */ + dma_mode = DMA_get_channel_mode(fdctrl->dma_chann); + dma_mode = (dma_mode >> 2) & 3; + FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n", + dma_mode, direction, + (128 << fdctrl->fifo[5]) * + (cur_drv->last_sect - ks + 1), fdctrl->data_len); + if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL || + direction == FD_DIR_SCANH) && dma_mode == 0) || + (direction == FD_DIR_WRITE && dma_mode == 2) || + (direction == FD_DIR_READ && dma_mode == 1)) { + /* No access is allowed until DMA transfer has completed */ + fdctrl->state |= FD_CTRL_BUSY; + /* Now, we just have to wait for the DMA controller to + * recall us... + */ + DMA_hold_DREQ(fdctrl->dma_chann); + DMA_schedule(fdctrl->dma_chann); + return; + } else { + FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction); + } + } + FLOPPY_DPRINTF("start non-DMA transfer\n"); + /* IO based transfer: calculate len */ + fdctrl_raise_irq(fdctrl, 0x00); + + return; +} + +/* Prepare a transfer of deleted data */ +static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction) +{ + /* We don't handle deleted data, + * so we don't return *ANYTHING* + */ + fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); +} + +/* handlers for DMA transfers */ +static int fdctrl_transfer_handler (void *opaque, int nchan, + int dma_pos, int dma_len) +{ + fdctrl_t *fdctrl; + fdrive_t *cur_drv; + int len, start_pos, rel_pos; + uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00; + + fdctrl = opaque; + if (!(fdctrl->state & FD_CTRL_BUSY)) { + FLOPPY_DPRINTF("Not in DMA transfer mode !\n"); + return 0; + } + cur_drv = get_cur_drv(fdctrl); + if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || + fdctrl->data_dir == FD_DIR_SCANH) + status2 = 0x04; + if (dma_len > fdctrl->data_len) + dma_len = fdctrl->data_len; + if (cur_drv->bs == NULL) { + if (fdctrl->data_dir == FD_DIR_WRITE) + fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); + else + fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); + len = 0; + goto transfer_error; + } + rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; + for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) { + len = dma_len - fdctrl->data_pos; + if (len + rel_pos > FD_SECTOR_LEN) + len = FD_SECTOR_LEN - rel_pos; + FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x " + "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos, + fdctrl->data_len, fdctrl->cur_drv, cur_drv->head, + cur_drv->track, cur_drv->sect, fd_sector(cur_drv), + fd_sector(cur_drv) * 512); + if (fdctrl->data_dir != FD_DIR_WRITE || + len < FD_SECTOR_LEN || rel_pos != 0) { + /* READ & SCAN commands and realign to a sector for WRITE */ + if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), + fdctrl->fifo, 1) < 0) { + FLOPPY_DPRINTF("Floppy: error getting sector %d\n", + fd_sector(cur_drv)); + /* Sure, image size is too small... */ + memset(fdctrl->fifo, 0, FD_SECTOR_LEN); + } + } + switch (fdctrl->data_dir) { + case FD_DIR_READ: + /* READ commands */ + DMA_write_memory (nchan, fdctrl->fifo + rel_pos, + fdctrl->data_pos, len); +/* cpu_physical_memory_write(addr + fdctrl->data_pos, */ +/* fdctrl->fifo + rel_pos, len); */ + break; + case FD_DIR_WRITE: + /* WRITE commands */ + DMA_read_memory (nchan, fdctrl->fifo + rel_pos, + fdctrl->data_pos, len); +/* cpu_physical_memory_read(addr + fdctrl->data_pos, */ +/* fdctrl->fifo + rel_pos, len); */ + if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), + fdctrl->fifo, 1) < 0) { + FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv)); + fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); + goto transfer_error; + } + break; + default: + /* SCAN commands */ + { + uint8_t tmpbuf[FD_SECTOR_LEN]; + int ret; + DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len); +/* cpu_physical_memory_read(addr + fdctrl->data_pos, */ +/* tmpbuf, len); */ + ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); + if (ret == 0) { + status2 = 0x08; + goto end_transfer; + } + if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) || + (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) { + status2 = 0x00; + goto end_transfer; + } + } + break; + } + fdctrl->data_pos += len; + rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; + if (rel_pos == 0) { + /* Seek to next sector */ + FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d) (%d)\n", + cur_drv->head, cur_drv->track, cur_drv->sect, + fd_sector(cur_drv), + fdctrl->data_pos - len); + /* XXX: cur_drv->sect >= cur_drv->last_sect should be an + error in fact */ + if (cur_drv->sect >= cur_drv->last_sect || + cur_drv->sect == fdctrl->eot) { + cur_drv->sect = 1; + if (FD_MULTI_TRACK(fdctrl->data_state)) { + if (cur_drv->head == 0 && + (cur_drv->flags & FDISK_DBL_SIDES) != 0) { + cur_drv->head = 1; + } else { + cur_drv->head = 0; + cur_drv->track++; + if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) + break; + } + } else { + cur_drv->track++; + break; + } + FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n", + cur_drv->head, cur_drv->track, + cur_drv->sect, fd_sector(cur_drv)); + } else { + cur_drv->sect++; + } + } + } +end_transfer: + len = fdctrl->data_pos - start_pos; + FLOPPY_DPRINTF("end transfer %d %d %d\n", + fdctrl->data_pos, len, fdctrl->data_len); + if (fdctrl->data_dir == FD_DIR_SCANE || + fdctrl->data_dir == FD_DIR_SCANL || + fdctrl->data_dir == FD_DIR_SCANH) + status2 = 0x08; + if (FD_DID_SEEK(fdctrl->data_state)) + status0 |= 0x20; + fdctrl->data_len -= len; + // if (fdctrl->data_len == 0) + fdctrl_stop_transfer(fdctrl, status0, status1, status2); +transfer_error: + + return len; +} + +/* Data register : 0x05 */ +static uint32_t fdctrl_read_data (fdctrl_t *fdctrl) +{ + fdrive_t *cur_drv; + uint32_t retval = 0; + int pos, len; + + cur_drv = get_cur_drv(fdctrl); + fdctrl->state &= ~FD_CTRL_SLEEP; + if (FD_STATE(fdctrl->data_state) == FD_STATE_CMD) { + FLOPPY_ERROR("can't read data in CMD state\n"); + return 0; + } + pos = fdctrl->data_pos; + if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { + pos %= FD_SECTOR_LEN; + if (pos == 0) { + len = fdctrl->data_len - fdctrl->data_pos; + if (len > FD_SECTOR_LEN) + len = FD_SECTOR_LEN; + bdrv_read(cur_drv->bs, fd_sector(cur_drv), + fdctrl->fifo, len); + } + } + retval = fdctrl->fifo[pos]; + if (++fdctrl->data_pos == fdctrl->data_len) { + fdctrl->data_pos = 0; + /* Switch from transfer mode to status mode + * then from status mode to command mode + */ + if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { + fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); + } else { + fdctrl_reset_fifo(fdctrl); + fdctrl_reset_irq(fdctrl); + } + } + FLOPPY_DPRINTF("data register: 0x%02x\n", retval); + + return retval; +} + +static void fdctrl_format_sector (fdctrl_t *fdctrl) +{ + fdrive_t *cur_drv; + uint8_t kh, kt, ks; + int did_seek; + + fdctrl->cur_drv = fdctrl->fifo[1] & 1; + cur_drv = get_cur_drv(fdctrl); + kt = fdctrl->fifo[6]; + kh = fdctrl->fifo[7]; + ks = fdctrl->fifo[8]; + FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n", + fdctrl->cur_drv, kh, kt, ks, + _fd_sector(kh, kt, ks, cur_drv->last_sect)); + did_seek = 0; + switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) { + case 2: + /* sect too big */ + fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; + case 3: + /* track too big */ + fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; + case 4: + /* No seek enabled */ + fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; + case 1: + did_seek = 1; + fdctrl->data_state |= FD_STATE_SEEK; + break; + default: + break; + } + memset(fdctrl->fifo, 0, FD_SECTOR_LEN); + if (cur_drv->bs == NULL || + bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { + FLOPPY_ERROR("formating sector %d\n", fd_sector(cur_drv)); + fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); + } else { + if (cur_drv->sect == cur_drv->last_sect) { + fdctrl->data_state &= ~FD_STATE_FORMAT; + /* Last sector done */ + if (FD_DID_SEEK(fdctrl->data_state)) + fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); + else + fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); + } else { + /* More to do */ + fdctrl->data_pos = 0; + fdctrl->data_len = 4; + } + } +} + +static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value) +{ + fdrive_t *cur_drv; + + cur_drv = get_cur_drv(fdctrl); + /* Reset mode */ + if (fdctrl->state & FD_CTRL_RESET) { + FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); + return; + } + fdctrl->state &= ~FD_CTRL_SLEEP; + if (FD_STATE(fdctrl->data_state) == FD_STATE_STATUS) { + FLOPPY_ERROR("can't write data in status mode\n"); + return; + } + /* Is it write command time ? */ + if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { + /* FIFO data write */ + fdctrl->fifo[fdctrl->data_pos++] = value; + if (fdctrl->data_pos % FD_SECTOR_LEN == (FD_SECTOR_LEN - 1) || + fdctrl->data_pos == fdctrl->data_len) { + bdrv_write(cur_drv->bs, fd_sector(cur_drv), + fdctrl->fifo, FD_SECTOR_LEN); + } + /* Switch from transfer mode to status mode + * then from status mode to command mode + */ + if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) + fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); + return; + } + if (fdctrl->data_pos == 0) { + /* Command */ + switch (value & 0x5F) { + case 0x46: + /* READ variants */ + FLOPPY_DPRINTF("READ command\n"); + /* 8 parameters cmd */ + fdctrl->data_len = 9; + goto enqueue; + case 0x4C: + /* READ_DELETED variants */ + FLOPPY_DPRINTF("READ_DELETED command\n"); + /* 8 parameters cmd */ + fdctrl->data_len = 9; + goto enqueue; + case 0x50: + /* SCAN_EQUAL variants */ + FLOPPY_DPRINTF("SCAN_EQUAL command\n"); + /* 8 parameters cmd */ + fdctrl->data_len = 9; + goto enqueue; + case 0x56: + /* VERIFY variants */ + FLOPPY_DPRINTF("VERIFY command\n"); + /* 8 parameters cmd */ + fdctrl->data_len = 9; + goto enqueue; + case 0x59: + /* SCAN_LOW_OR_EQUAL variants */ + FLOPPY_DPRINTF("SCAN_LOW_OR_EQUAL command\n"); + /* 8 parameters cmd */ + fdctrl->data_len = 9; + goto enqueue; + case 0x5D: + /* SCAN_HIGH_OR_EQUAL variants */ + FLOPPY_DPRINTF("SCAN_HIGH_OR_EQUAL command\n"); + /* 8 parameters cmd */ + fdctrl->data_len = 9; + goto enqueue; + default: + break; + } + switch (value & 0x7F) { + case 0x45: + /* WRITE variants */ + FLOPPY_DPRINTF("WRITE command\n"); + /* 8 parameters cmd */ + fdctrl->data_len = 9; + goto enqueue; + case 0x49: + /* WRITE_DELETED variants */ + FLOPPY_DPRINTF("WRITE_DELETED command\n"); + /* 8 parameters cmd */ + fdctrl->data_len = 9; + goto enqueue; + default: + break; + } + switch (value) { + case 0x03: + /* SPECIFY */ + FLOPPY_DPRINTF("SPECIFY command\n"); + /* 1 parameter cmd */ + fdctrl->data_len = 3; + goto enqueue; + case 0x04: + /* SENSE_DRIVE_STATUS */ + FLOPPY_DPRINTF("SENSE_DRIVE_STATUS command\n"); + /* 1 parameter cmd */ + fdctrl->data_len = 2; + goto enqueue; + case 0x07: + /* RECALIBRATE */ + FLOPPY_DPRINTF("RECALIBRATE command\n"); + /* 1 parameter cmd */ + fdctrl->data_len = 2; + goto enqueue; + case 0x08: + /* SENSE_INTERRUPT_STATUS */ + FLOPPY_DPRINTF("SENSE_INTERRUPT_STATUS command (%02x)\n", + fdctrl->int_status); + /* No parameters cmd: returns status if no interrupt */ +#if 0 + fdctrl->fifo[0] = + fdctrl->int_status | (cur_drv->head << 2) | fdctrl->cur_drv; +#else + /* XXX: int_status handling is broken for read/write + commands, so we do this hack. It should be suppressed + ASAP */ + fdctrl->fifo[0] = + 0x20 | (cur_drv->head << 2) | fdctrl->cur_drv; +#endif + fdctrl->fifo[1] = cur_drv->track; + fdctrl_set_fifo(fdctrl, 2, 0); + fdctrl_reset_irq(fdctrl); + fdctrl->int_status = 0xC0; + return; + case 0x0E: + /* DUMPREG */ + FLOPPY_DPRINTF("DUMPREG command\n"); + /* Drives position */ + fdctrl->fifo[0] = drv0(fdctrl)->track; + fdctrl->fifo[1] = drv1(fdctrl)->track; + fdctrl->fifo[2] = 0; + fdctrl->fifo[3] = 0; + /* timers */ + fdctrl->fifo[4] = fdctrl->timer0; + fdctrl->fifo[5] = (fdctrl->timer1 << 1) | fdctrl->dma_en; + fdctrl->fifo[6] = cur_drv->last_sect; + fdctrl->fifo[7] = (fdctrl->lock << 7) | + (cur_drv->perpendicular << 2); + fdctrl->fifo[8] = fdctrl->config; + fdctrl->fifo[9] = fdctrl->precomp_trk; + fdctrl_set_fifo(fdctrl, 10, 0); + return; + case 0x0F: + /* SEEK */ + FLOPPY_DPRINTF("SEEK command\n"); + /* 2 parameters cmd */ + fdctrl->data_len = 3; + goto enqueue; + case 0x10: + /* VERSION */ + FLOPPY_DPRINTF("VERSION command\n"); + /* No parameters cmd */ + /* Controller's version */ + fdctrl->fifo[0] = fdctrl->version; + fdctrl_set_fifo(fdctrl, 1, 1); + return; + case 0x12: + /* PERPENDICULAR_MODE */ + FLOPPY_DPRINTF("PERPENDICULAR_MODE command\n"); + /* 1 parameter cmd */ + fdctrl->data_len = 2; + goto enqueue; + case 0x13: + /* CONFIGURE */ + FLOPPY_DPRINTF("CONFIGURE command\n"); + /* 3 parameters cmd */ + fdctrl->data_len = 4; + goto enqueue; + case 0x14: + /* UNLOCK */ + FLOPPY_DPRINTF("UNLOCK command\n"); + /* No parameters cmd */ + fdctrl->lock = 0; + fdctrl->fifo[0] = 0; + fdctrl_set_fifo(fdctrl, 1, 0); + return; + case 0x17: + /* POWERDOWN_MODE */ + FLOPPY_DPRINTF("POWERDOWN_MODE command\n"); + /* 2 parameters cmd */ + fdctrl->data_len = 3; + goto enqueue; + case 0x18: + /* PART_ID */ + FLOPPY_DPRINTF("PART_ID command\n"); + /* No parameters cmd */ + fdctrl->fifo[0] = 0x41; /* Stepping 1 */ + fdctrl_set_fifo(fdctrl, 1, 0); + return; + case 0x2C: + /* SAVE */ + FLOPPY_DPRINTF("SAVE command\n"); + /* No parameters cmd */ + fdctrl->fifo[0] = 0; + fdctrl->fifo[1] = 0; + /* Drives position */ + fdctrl->fifo[2] = drv0(fdctrl)->track; + fdctrl->fifo[3] = drv1(fdctrl)->track; + fdctrl->fifo[4] = 0; + fdctrl->fifo[5] = 0; + /* timers */ + fdctrl->fifo[6] = fdctrl->timer0; + fdctrl->fifo[7] = fdctrl->timer1; + fdctrl->fifo[8] = cur_drv->last_sect; + fdctrl->fifo[9] = (fdctrl->lock << 7) | + (cur_drv->perpendicular << 2); + fdctrl->fifo[10] = fdctrl->config; + fdctrl->fifo[11] = fdctrl->precomp_trk; + fdctrl->fifo[12] = fdctrl->pwrd; + fdctrl->fifo[13] = 0; + fdctrl->fifo[14] = 0; + fdctrl_set_fifo(fdctrl, 15, 1); + return; + case 0x33: + /* OPTION */ + FLOPPY_DPRINTF("OPTION command\n"); + /* 1 parameter cmd */ + fdctrl->data_len = 2; + goto enqueue; + case 0x42: + /* READ_TRACK */ + FLOPPY_DPRINTF("READ_TRACK command\n"); + /* 8 parameters cmd */ + fdctrl->data_len = 9; + goto enqueue; + case 0x4A: + /* READ_ID */ + FLOPPY_DPRINTF("READ_ID command\n"); + /* 1 parameter cmd */ + fdctrl->data_len = 2; + goto enqueue; + case 0x4C: + /* RESTORE */ + FLOPPY_DPRINTF("RESTORE command\n"); + /* 17 parameters cmd */ + fdctrl->data_len = 18; + goto enqueue; + case 0x4D: + /* FORMAT_TRACK */ + FLOPPY_DPRINTF("FORMAT_TRACK command\n"); + /* 5 parameters cmd */ + fdctrl->data_len = 6; + goto enqueue; + case 0x8E: + /* DRIVE_SPECIFICATION_COMMAND */ + FLOPPY_DPRINTF("DRIVE_SPECIFICATION_COMMAND command\n"); + /* 5 parameters cmd */ + fdctrl->data_len = 6; + goto enqueue; + case 0x8F: + /* RELATIVE_SEEK_OUT */ + FLOPPY_DPRINTF("RELATIVE_SEEK_OUT command\n"); + /* 2 parameters cmd */ + fdctrl->data_len = 3; + goto enqueue; + case 0x94: + /* LOCK */ + FLOPPY_DPRINTF("LOCK command\n"); + /* No parameters cmd */ + fdctrl->lock = 1; + fdctrl->fifo[0] = 0x10; + fdctrl_set_fifo(fdctrl, 1, 1); + return; + case 0xCD: + /* FORMAT_AND_WRITE */ + FLOPPY_DPRINTF("FORMAT_AND_WRITE command\n"); + /* 10 parameters cmd */ + fdctrl->data_len = 11; + goto enqueue; + case 0xCF: + /* RELATIVE_SEEK_IN */ + FLOPPY_DPRINTF("RELATIVE_SEEK_IN command\n"); + /* 2 parameters cmd */ + fdctrl->data_len = 3; + goto enqueue; + default: + /* Unknown command */ + FLOPPY_ERROR("unknown command: 0x%02x\n", value); + fdctrl_unimplemented(fdctrl); + return; + } + } +enqueue: + FLOPPY_DPRINTF("%s: %02x\n", __func__, value); + fdctrl->fifo[fdctrl->data_pos] = value; + if (++fdctrl->data_pos == fdctrl->data_len) { + /* We now have all parameters + * and will be able to treat the command + */ + if (fdctrl->data_state & FD_STATE_FORMAT) { + fdctrl_format_sector(fdctrl); + return; + } + switch (fdctrl->fifo[0] & 0x1F) { + case 0x06: + { + /* READ variants */ + FLOPPY_DPRINTF("treat READ command\n"); + fdctrl_start_transfer(fdctrl, FD_DIR_READ); + return; + } + case 0x0C: + /* READ_DELETED variants */ +// FLOPPY_DPRINTF("treat READ_DELETED command\n"); + FLOPPY_ERROR("treat READ_DELETED command\n"); + fdctrl_start_transfer_del(fdctrl, FD_DIR_READ); + return; + case 0x16: + /* VERIFY variants */ +// FLOPPY_DPRINTF("treat VERIFY command\n"); + FLOPPY_ERROR("treat VERIFY command\n"); + fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); + return; + case 0x10: + /* SCAN_EQUAL variants */ +// FLOPPY_DPRINTF("treat SCAN_EQUAL command\n"); + FLOPPY_ERROR("treat SCAN_EQUAL command\n"); + fdctrl_start_transfer(fdctrl, FD_DIR_SCANE); + return; + case 0x19: + /* SCAN_LOW_OR_EQUAL variants */ +// FLOPPY_DPRINTF("treat SCAN_LOW_OR_EQUAL command\n"); + FLOPPY_ERROR("treat SCAN_LOW_OR_EQUAL command\n"); + fdctrl_start_transfer(fdctrl, FD_DIR_SCANL); + return; + case 0x1D: + /* SCAN_HIGH_OR_EQUAL variants */ +// FLOPPY_DPRINTF("treat SCAN_HIGH_OR_EQUAL command\n"); + FLOPPY_ERROR("treat SCAN_HIGH_OR_EQUAL command\n"); + fdctrl_start_transfer(fdctrl, FD_DIR_SCANH); + return; + default: + break; + } + switch (fdctrl->fifo[0] & 0x3F) { + case 0x05: + /* WRITE variants */ + FLOPPY_DPRINTF("treat WRITE command (%02x)\n", fdctrl->fifo[0]); + fdctrl_start_transfer(fdctrl, FD_DIR_WRITE); + return; + case 0x09: + /* WRITE_DELETED variants */ +// FLOPPY_DPRINTF("treat WRITE_DELETED command\n"); + FLOPPY_ERROR("treat WRITE_DELETED command\n"); + fdctrl_start_transfer_del(fdctrl, FD_DIR_WRITE); + return; + default: + break; + } + switch (fdctrl->fifo[0]) { + case 0x03: + /* SPECIFY */ + FLOPPY_DPRINTF("treat SPECIFY command\n"); + fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF; + fdctrl->timer1 = fdctrl->fifo[2] >> 1; + fdctrl->dma_en = 1 - (fdctrl->fifo[2] & 1) ; + /* No result back */ + fdctrl_reset_fifo(fdctrl); + break; + case 0x04: + /* SENSE_DRIVE_STATUS */ + FLOPPY_DPRINTF("treat SENSE_DRIVE_STATUS command\n"); + fdctrl->cur_drv = fdctrl->fifo[1] & 1; + cur_drv = get_cur_drv(fdctrl); + cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; + /* 1 Byte status back */ + fdctrl->fifo[0] = (cur_drv->ro << 6) | + (cur_drv->track == 0 ? 0x10 : 0x00) | + (cur_drv->head << 2) | + fdctrl->cur_drv | + 0x28; + fdctrl_set_fifo(fdctrl, 1, 0); + break; + case 0x07: + /* RECALIBRATE */ + FLOPPY_DPRINTF("treat RECALIBRATE command\n"); + fdctrl->cur_drv = fdctrl->fifo[1] & 1; + cur_drv = get_cur_drv(fdctrl); + fd_recalibrate(cur_drv); + fdctrl_reset_fifo(fdctrl); + /* Raise Interrupt */ + fdctrl_raise_irq(fdctrl, 0x20); + break; + case 0x0F: + /* SEEK */ + FLOPPY_DPRINTF("treat SEEK command\n"); + fdctrl->cur_drv = fdctrl->fifo[1] & 1; + cur_drv = get_cur_drv(fdctrl); + fd_start(cur_drv); + if (fdctrl->fifo[2] <= cur_drv->track) + cur_drv->dir = 1; + else + cur_drv->dir = 0; + fdctrl_reset_fifo(fdctrl); + if (fdctrl->fifo[2] > cur_drv->max_track) { + fdctrl_raise_irq(fdctrl, 0x60); + } else { + cur_drv->track = fdctrl->fifo[2]; + /* Raise Interrupt */ + fdctrl_raise_irq(fdctrl, 0x20); + } + break; + case 0x12: + /* PERPENDICULAR_MODE */ + FLOPPY_DPRINTF("treat PERPENDICULAR_MODE command\n"); + if (fdctrl->fifo[1] & 0x80) + cur_drv->perpendicular = fdctrl->fifo[1] & 0x7; + /* No result back */ + fdctrl_reset_fifo(fdctrl); + break; + case 0x13: + /* CONFIGURE */ + FLOPPY_DPRINTF("treat CONFIGURE command\n"); + fdctrl->config = fdctrl->fifo[2]; + fdctrl->precomp_trk = fdctrl->fifo[3]; + /* No result back */ + fdctrl_reset_fifo(fdctrl); + break; + case 0x17: + /* POWERDOWN_MODE */ + FLOPPY_DPRINTF("treat POWERDOWN_MODE command\n"); + fdctrl->pwrd = fdctrl->fifo[1]; + fdctrl->fifo[0] = fdctrl->fifo[1]; + fdctrl_set_fifo(fdctrl, 1, 1); + break; + case 0x33: + /* OPTION */ + FLOPPY_DPRINTF("treat OPTION command\n"); + /* No result back */ + fdctrl_reset_fifo(fdctrl); + break; + case 0x42: + /* READ_TRACK */ +// FLOPPY_DPRINTF("treat READ_TRACK command\n"); + FLOPPY_ERROR("treat READ_TRACK command\n"); + fdctrl_start_transfer(fdctrl, FD_DIR_READ); + break; + case 0x4A: + /* READ_ID */ + FLOPPY_DPRINTF("treat READ_ID command\n"); + /* XXX: should set main status register to busy */ + cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; + qemu_mod_timer(fdctrl->result_timer, + qemu_get_clock(vm_clock) + (ticks_per_sec / 50)); + break; + case 0x4C: + /* RESTORE */ + FLOPPY_DPRINTF("treat RESTORE command\n"); + /* Drives position */ + drv0(fdctrl)->track = fdctrl->fifo[3]; + drv1(fdctrl)->track = fdctrl->fifo[4]; + /* timers */ + fdctrl->timer0 = fdctrl->fifo[7]; + fdctrl->timer1 = fdctrl->fifo[8]; + cur_drv->last_sect = fdctrl->fifo[9]; + fdctrl->lock = fdctrl->fifo[10] >> 7; + cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF; + fdctrl->config = fdctrl->fifo[11]; + fdctrl->precomp_trk = fdctrl->fifo[12]; + fdctrl->pwrd = fdctrl->fifo[13]; + fdctrl_reset_fifo(fdctrl); + break; + case 0x4D: + /* FORMAT_TRACK */ + FLOPPY_DPRINTF("treat FORMAT_TRACK command\n"); + fdctrl->cur_drv = fdctrl->fifo[1] & 1; + cur_drv = get_cur_drv(fdctrl); + fdctrl->data_state |= FD_STATE_FORMAT; + if (fdctrl->fifo[0] & 0x80) + fdctrl->data_state |= FD_STATE_MULTI; + else + fdctrl->data_state &= ~FD_STATE_MULTI; + fdctrl->data_state &= ~FD_STATE_SEEK; + cur_drv->bps = + fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2]; +#if 0 + cur_drv->last_sect = + cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] : + fdctrl->fifo[3] / 2; +#else + cur_drv->last_sect = fdctrl->fifo[3]; +#endif + /* Bochs BIOS is buggy and don't send format informations + * for each sector. So, pretend all's done right now... + */ + fdctrl->data_state &= ~FD_STATE_FORMAT; + fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); + break; + case 0x8E: + /* DRIVE_SPECIFICATION_COMMAND */ + FLOPPY_DPRINTF("treat DRIVE_SPECIFICATION_COMMAND command\n"); + if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) { + /* Command parameters done */ + if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) { + fdctrl->fifo[0] = fdctrl->fifo[1]; + fdctrl->fifo[2] = 0; + fdctrl->fifo[3] = 0; + fdctrl_set_fifo(fdctrl, 4, 1); + } else { + fdctrl_reset_fifo(fdctrl); + } + } else if (fdctrl->data_len > 7) { + /* ERROR */ + fdctrl->fifo[0] = 0x80 | + (cur_drv->head << 2) | fdctrl->cur_drv; + fdctrl_set_fifo(fdctrl, 1, 1); + } + break; + case 0x8F: + /* RELATIVE_SEEK_OUT */ + FLOPPY_DPRINTF("treat RELATIVE_SEEK_OUT command\n"); + fdctrl->cur_drv = fdctrl->fifo[1] & 1; + cur_drv = get_cur_drv(fdctrl); + fd_start(cur_drv); + cur_drv->dir = 0; + if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) { + cur_drv->track = cur_drv->max_track - 1; + } else { + cur_drv->track += fdctrl->fifo[2]; + } + fdctrl_reset_fifo(fdctrl); + fdctrl_raise_irq(fdctrl, 0x20); + break; + case 0xCD: + /* FORMAT_AND_WRITE */ +// FLOPPY_DPRINTF("treat FORMAT_AND_WRITE command\n"); + FLOPPY_ERROR("treat FORMAT_AND_WRITE command\n"); + fdctrl_unimplemented(fdctrl); + break; + case 0xCF: + /* RELATIVE_SEEK_IN */ + FLOPPY_DPRINTF("treat RELATIVE_SEEK_IN command\n"); + fdctrl->cur_drv = fdctrl->fifo[1] & 1; + cur_drv = get_cur_drv(fdctrl); + fd_start(cur_drv); + cur_drv->dir = 1; + if (fdctrl->fifo[2] > cur_drv->track) { + cur_drv->track = 0; + } else { + cur_drv->track -= fdctrl->fifo[2]; + } + fdctrl_reset_fifo(fdctrl); + /* Raise Interrupt */ + fdctrl_raise_irq(fdctrl, 0x20); + break; + } + } +} + +static void fdctrl_result_timer(void *opaque) +{ + fdctrl_t *fdctrl = opaque; + fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); +} diff --git a/tools/ioemu/hw/fmopl.c b/tools/ioemu/hw/fmopl.c new file mode 100644 index 0000000000..2b0e82b0cc --- /dev/null +++ b/tools/ioemu/hw/fmopl.c @@ -0,0 +1,1390 @@ +/* +** +** File: fmopl.c -- software implementation of FM sound generator +** +** Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmurator development +** +** Version 0.37a +** +*/ + +/* + preliminary : + Problem : + note: +*/ + +/* This version of fmopl.c is a fork of the MAME one, relicensed under the LGPL. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define INLINE __inline +#define HAS_YM3812 1 + +#include +#include +#include +#include +#include +//#include "driver.h" /* use M.A.M.E. */ +#include "fmopl.h" + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/* -------------------- for debug --------------------- */ +/* #define OPL_OUTPUT_LOG */ +#ifdef OPL_OUTPUT_LOG +static FILE *opl_dbg_fp = NULL; +static FM_OPL *opl_dbg_opl[16]; +static int opl_dbg_maxchip,opl_dbg_chip; +#endif + +/* -------------------- preliminary define section --------------------- */ +/* attack/decay rate time rate */ +#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */ +#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */ + +#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */ + +#define FREQ_BITS 24 /* frequency turn */ + +/* counter bits = 20 , octerve 7 */ +#define FREQ_RATE (1<<(FREQ_BITS-20)) +#define TL_BITS (FREQ_BITS+2) + +/* final output shift , limit minimum and maximum */ +#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */ +#define OPL_MAXOUT (0x7fff<=LOG_LEVEL ) logerror x +#define LOG(n,x) + +/* --------------------- subroutines --------------------- */ + +INLINE int Limit( int val, int max, int min ) { + if ( val > max ) + val = max; + else if ( val < min ) + val = min; + + return val; +} + +/* status set and IRQ handling */ +INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag) +{ + /* set status flag */ + OPL->status |= flag; + if(!(OPL->status & 0x80)) + { + if(OPL->status & OPL->statusmask) + { /* IRQ on */ + OPL->status |= 0x80; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1); + } + } +} + +/* status reset and IRQ handling */ +INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag) +{ + /* reset status flag */ + OPL->status &=~flag; + if((OPL->status & 0x80)) + { + if (!(OPL->status & OPL->statusmask) ) + { + OPL->status &= 0x7f; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0); + } + } +} + +/* IRQ mask set */ +INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag) +{ + OPL->statusmask = flag; + /* IRQ handling check */ + OPL_STATUS_SET(OPL,0); + OPL_STATUS_RESET(OPL,0); +} + +/* ----- key on ----- */ +INLINE void OPL_KEYON(OPL_SLOT *SLOT) +{ + /* sin wave restart */ + SLOT->Cnt = 0; + /* set attack */ + SLOT->evm = ENV_MOD_AR; + SLOT->evs = SLOT->evsa; + SLOT->evc = EG_AST; + SLOT->eve = EG_AED; +} +/* ----- key off ----- */ +INLINE void OPL_KEYOFF(OPL_SLOT *SLOT) +{ + if( SLOT->evm > ENV_MOD_RR) + { + /* set envelope counter from envleope output */ + SLOT->evm = ENV_MOD_RR; + if( !(SLOT->evc&EG_DST) ) + //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<evc = EG_DST; + SLOT->eve = EG_DED; + SLOT->evs = SLOT->evsr; + } +} + +/* ---------- calcrate Envelope Generator & Phase Generator ---------- */ +/* return : envelope output */ +INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT ) +{ + /* calcrate envelope generator */ + if( (SLOT->evc+=SLOT->evs) >= SLOT->eve ) + { + switch( SLOT->evm ){ + case ENV_MOD_AR: /* ATTACK -> DECAY1 */ + /* next DR */ + SLOT->evm = ENV_MOD_DR; + SLOT->evc = EG_DST; + SLOT->eve = SLOT->SL; + SLOT->evs = SLOT->evsd; + break; + case ENV_MOD_DR: /* DECAY -> SL or RR */ + SLOT->evc = SLOT->SL; + SLOT->eve = EG_DED; + if(SLOT->eg_typ) + { + SLOT->evs = 0; + } + else + { + SLOT->evm = ENV_MOD_RR; + SLOT->evs = SLOT->evsr; + } + break; + case ENV_MOD_RR: /* RR -> OFF */ + SLOT->evc = EG_OFF; + SLOT->eve = EG_OFF+1; + SLOT->evs = 0; + break; + } + } + /* calcrate envelope */ + return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0); +} + +/* set algorythm connection */ +static void set_algorythm( OPL_CH *CH) +{ + INT32 *carrier = &outd[0]; + CH->connect1 = CH->CON ? carrier : &feedback2; + CH->connect2 = carrier; +} + +/* ---------- frequency counter for operater update ---------- */ +INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) +{ + int ksr; + + /* frequency step counter */ + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + /* attack , decay rate recalcration */ + SLOT->evsa = SLOT->AR[ksr]; + SLOT->evsd = SLOT->DR[ksr]; + SLOT->evsr = SLOT->RR[ksr]; + } + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); +} + +/* set multi,am,vib,EG-TYP,KSR,mul */ +INLINE void set_mul(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->mul = MUL_TABLE[v&0x0f]; + SLOT->KSR = (v&0x10) ? 0 : 2; + SLOT->eg_typ = (v&0x20)>>5; + SLOT->vib = (v&0x40); + SLOT->ams = (v&0x80); + CALC_FCSLOT(CH,SLOT); +} + +/* set ksl & tl */ +INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */ + + SLOT->ksl = ksl ? 3-ksl : 31; + SLOT->TL = (v&0x3f)*(0.75/EG_STEP); /* 0.75db step */ + + if( !(OPL->mode&0x80) ) + { /* not CSM latch total level */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } +} + +/* set attack rate & decay rate */ +INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + int ar = v>>4; + int dr = v&0x0f; + + SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0; + SLOT->evsa = SLOT->AR[SLOT->ksr]; + if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa; + + SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0; + SLOT->evsd = SLOT->DR[SLOT->ksr]; + if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd; +} + +/* set sustain level & release rate */ +INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + int sl = v>>4; + int rr = v & 0x0f; + + SLOT->SL = SL_TABLE[sl]; + if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL; + SLOT->RR = &OPL->DR_TABLE[rr<<2]; + SLOT->evsr = SLOT->RR[SLOT->ksr]; + if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr; +} + +/* operator output calcrator */ +#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env] +/* ---------- calcrate one of channel ---------- */ +INLINE void OPL_CALC_CH( OPL_CH *CH ) +{ + UINT32 env_out; + OPL_SLOT *SLOT; + + feedback2 = 0; + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env_out=OPL_CALC_SLOT(SLOT); + if( env_out < EG_ENT-1 ) + { + /* PG */ + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); + else SLOT->Cnt += SLOT->Incr; + /* connectoion */ + if(CH->FB) + { + int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB; + CH->op1_out[1] = CH->op1_out[0]; + *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1); + } + else + { + *CH->connect1 += OP_OUT(SLOT,env_out,0); + } + }else + { + CH->op1_out[1] = CH->op1_out[0]; + CH->op1_out[0] = 0; + } + /* SLOT 2 */ + SLOT = &CH->SLOT[SLOT2]; + env_out=OPL_CALC_SLOT(SLOT); + if( env_out < EG_ENT-1 ) + { + /* PG */ + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); + else SLOT->Cnt += SLOT->Incr; + /* connectoion */ + outd[0] += OP_OUT(SLOT,env_out, feedback2); + } +} + +/* ---------- calcrate rythm block ---------- */ +#define WHITE_NOISE_db 6.0 +INLINE void OPL_CALC_RH( OPL_CH *CH ) +{ + UINT32 env_tam,env_sd,env_top,env_hh; + int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP); + INT32 tone8; + + OPL_SLOT *SLOT; + int env_out; + + /* BD : same as FM serial mode and output level is large */ + feedback2 = 0; + /* SLOT 1 */ + SLOT = &CH[6].SLOT[SLOT1]; + env_out=OPL_CALC_SLOT(SLOT); + if( env_out < EG_ENT-1 ) + { + /* PG */ + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); + else SLOT->Cnt += SLOT->Incr; + /* connectoion */ + if(CH[6].FB) + { + int feedback1 = (CH[6].op1_out[0]+CH[6].op1_out[1])>>CH[6].FB; + CH[6].op1_out[1] = CH[6].op1_out[0]; + feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT,env_out,feedback1); + } + else + { + feedback2 = OP_OUT(SLOT,env_out,0); + } + }else + { + feedback2 = 0; + CH[6].op1_out[1] = CH[6].op1_out[0]; + CH[6].op1_out[0] = 0; + } + /* SLOT 2 */ + SLOT = &CH[6].SLOT[SLOT2]; + env_out=OPL_CALC_SLOT(SLOT); + if( env_out < EG_ENT-1 ) + { + /* PG */ + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); + else SLOT->Cnt += SLOT->Incr; + /* connectoion */ + outd[0] += OP_OUT(SLOT,env_out, feedback2)*2; + } + + // SD (17) = mul14[fnum7] + white noise + // TAM (15) = mul15[fnum8] + // TOP (18) = fnum6(mul18[fnum8]+whitenoise) + // HH (14) = fnum7(mul18[fnum8]+whitenoise) + white noise + env_sd =OPL_CALC_SLOT(SLOT7_2) + whitenoise; + env_tam=OPL_CALC_SLOT(SLOT8_1); + env_top=OPL_CALC_SLOT(SLOT8_2); + env_hh =OPL_CALC_SLOT(SLOT7_1) + whitenoise; + + /* PG */ + if(SLOT7_1->vib) SLOT7_1->Cnt += (2*SLOT7_1->Incr*vib/VIB_RATE); + else SLOT7_1->Cnt += 2*SLOT7_1->Incr; + if(SLOT7_2->vib) SLOT7_2->Cnt += ((CH[7].fc*8)*vib/VIB_RATE); + else SLOT7_2->Cnt += (CH[7].fc*8); + if(SLOT8_1->vib) SLOT8_1->Cnt += (SLOT8_1->Incr*vib/VIB_RATE); + else SLOT8_1->Cnt += SLOT8_1->Incr; + if(SLOT8_2->vib) SLOT8_2->Cnt += ((CH[8].fc*48)*vib/VIB_RATE); + else SLOT8_2->Cnt += (CH[8].fc*48); + + tone8 = OP_OUT(SLOT8_2,whitenoise,0 ); + + /* SD */ + if( env_sd < EG_ENT-1 ) + outd[0] += OP_OUT(SLOT7_1,env_sd, 0)*8; + /* TAM */ + if( env_tam < EG_ENT-1 ) + outd[0] += OP_OUT(SLOT8_1,env_tam, 0)*2; + /* TOP-CY */ + if( env_top < EG_ENT-1 ) + outd[0] += OP_OUT(SLOT7_2,env_top,tone8)*2; + /* HH */ + if( env_hh < EG_ENT-1 ) + outd[0] += OP_OUT(SLOT7_2,env_hh,tone8)*2; +} + +/* ----------- initialize time tabls ----------- */ +static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE ) +{ + int i; + double rate; + + /* make attack rate & decay rate tables */ + for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0; + for (i = 4;i <= 60;i++){ + rate = OPL->freqbase; /* frequency rate */ + if( i < 60 ) rate *= 1.0+(i&3)*0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */ + rate *= 1<<((i>>2)-1); /* b2-5 : shift bit */ + rate *= (double)(EG_ENT<AR_TABLE[i] = rate / ARRATE; + OPL->DR_TABLE[i] = rate / DRRATE; + } + for (i = 60;i < 76;i++) + { + OPL->AR_TABLE[i] = EG_AED-1; + OPL->DR_TABLE[i] = OPL->DR_TABLE[60]; + } +#if 0 + for (i = 0;i < 64 ;i++){ /* make for overflow area */ + LOG(LOG_WAR,("rate %2d , ar %f ms , dr %f ms \n",i, + ((double)(EG_ENT<AR_TABLE[i]) * (1000.0 / OPL->rate), + ((double)(EG_ENT<DR_TABLE[i]) * (1000.0 / OPL->rate) )); + } +#endif +} + +/* ---------- generic table initialize ---------- */ +static int OPLOpenTable( void ) +{ + int s,t; + double rate; + int i,j; + double pom; + + /* allocate dynamic tables */ + if( (TL_TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL) + return 0; + if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL) + { + free(TL_TABLE); + return 0; + } + if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL) + { + free(TL_TABLE); + free(SIN_TABLE); + return 0; + } + if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL) + { + free(TL_TABLE); + free(SIN_TABLE); + free(AMS_TABLE); + return 0; + } + /* make total level table */ + for (t = 0;t < EG_ENT-1 ;t++){ + rate = ((1< voltage */ + TL_TABLE[ t] = (int)rate; + TL_TABLE[TL_MAX+t] = -TL_TABLE[t]; +/* LOG(LOG_INF,("TotalLevel(%3d) = %x\n",t,TL_TABLE[t]));*/ + } + /* fill volume off area */ + for ( t = EG_ENT-1; t < TL_MAX ;t++){ + TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0; + } + + /* make sinwave table (total level offet) */ + /* degree 0 = degree 180 = off */ + SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[EG_ENT-1]; + for (s = 1;s <= SIN_ENT/4;s++){ + pom = sin(2*PI*s/SIN_ENT); /* sin */ + pom = 20*log10(1/pom); /* decibel */ + j = pom / EG_STEP; /* TL_TABLE steps */ + + /* degree 0 - 90 , degree 180 - 90 : plus section */ + SIN_TABLE[ s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j]; + /* degree 180 - 270 , degree 360 - 270 : minus section */ + SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT -s] = &TL_TABLE[TL_MAX+j]; +/* LOG(LOG_INF,("sin(%3d) = %f:%f db\n",s,pom,(double)j * EG_STEP));*/ + } + for (s = 0;s < SIN_ENT;s++) + { + SIN_TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT]; + SIN_TABLE[SIN_ENT*2+s] = SIN_TABLE[s % (SIN_ENT/2)]; + SIN_TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT*2+s]; + } + + /* envelope counter -> envelope output table */ + for (i=0; i= EG_ENT ) pom = EG_ENT-1; */ + ENV_CURVE[i] = (int)pom; + /* DECAY ,RELEASE curve */ + ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i; + } + /* off */ + ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1; + /* make LFO ams table */ + for (i=0; iSLOT[SLOT1]; + OPL_SLOT *slot2 = &CH->SLOT[SLOT2]; + /* all key off */ + OPL_KEYOFF(slot1); + OPL_KEYOFF(slot2); + /* total level latch */ + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); + /* key on */ + CH->op1_out[0] = CH->op1_out[1] = 0; + OPL_KEYON(slot1); + OPL_KEYON(slot2); +} + +/* ---------- opl initialize ---------- */ +static void OPL_initalize(FM_OPL *OPL) +{ + int fn; + + /* frequency base */ + OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0; + /* Timer base time */ + OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 ); + /* make time tables */ + init_timetables( OPL , OPL_ARRATE , OPL_DRRATE ); + /* make fnumber -> increment counter table */ + for( fn=0 ; fn < 1024 ; fn++ ) + { + OPL->FN_TABLE[fn] = OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2; + } + /* LFO freq.table */ + OPL->amsIncr = OPL->rate ? (double)AMS_ENT*(1<rate * 3.7 * ((double)OPL->clock/3600000) : 0; + OPL->vibIncr = OPL->rate ? (double)VIB_ENT*(1<rate * 6.4 * ((double)OPL->clock/3600000) : 0; +} + +/* ---------- write a OPL registers ---------- */ +static void OPLWriteReg(FM_OPL *OPL, int r, int v) +{ + OPL_CH *CH; + int slot; + int block_fnum; + + switch(r&0xe0) + { + case 0x00: /* 00-1f:controll */ + switch(r&0x1f) + { + case 0x01: + /* wave selector enable */ + if(OPL->type&OPL_TYPE_WAVESEL) + { + OPL->wavesel = v&0x20; + if(!OPL->wavesel) + { + /* preset compatible mode */ + int c; + for(c=0;cmax_ch;c++) + { + OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0]; + OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0]; + } + } + } + return; + case 0x02: /* Timer 1 */ + OPL->T[0] = (256-v)*4; + break; + case 0x03: /* Timer 2 */ + OPL->T[1] = (256-v)*16; + return; + case 0x04: /* IRQ clear / mask and Timer enable */ + if(v&0x80) + { /* IRQ flag clear */ + OPL_STATUS_RESET(OPL,0x7f); + } + else + { /* set IRQ mask ,timer enable*/ + UINT8 st1 = v&1; + UINT8 st2 = (v>>1)&1; + /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ + OPL_STATUS_RESET(OPL,v&0x78); + OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01); + /* timer 2 */ + if(OPL->st[1] != st2) + { + double interval = st2 ? (double)OPL->T[1]*OPL->TimerBase : 0.0; + OPL->st[1] = st2; + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval); + } + /* timer 1 */ + if(OPL->st[0] != st1) + { + double interval = st1 ? (double)OPL->T[0]*OPL->TimerBase : 0.0; + OPL->st[0] = st1; + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval); + } + } + return; +#if BUILD_Y8950 + case 0x06: /* Key Board OUT */ + if(OPL->type&OPL_TYPE_KEYBOARD) + { + if(OPL->keyboardhandler_w) + OPL->keyboardhandler_w(OPL->keyboard_param,v); + else + LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n")); + } + return; + case 0x07: /* DELTA-T controll : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ + if(OPL->type&OPL_TYPE_ADPCM) + YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); + return; + case 0x08: /* MODE,DELTA-T : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */ + OPL->mode = v; + v&=0x1f; /* for DELTA-T unit */ + case 0x09: /* START ADD */ + case 0x0a: + case 0x0b: /* STOP ADD */ + case 0x0c: + case 0x0d: /* PRESCALE */ + case 0x0e: + case 0x0f: /* ADPCM data */ + case 0x10: /* DELTA-N */ + case 0x11: /* DELTA-N */ + case 0x12: /* EG-CTRL */ + if(OPL->type&OPL_TYPE_ADPCM) + YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); + return; +#if 0 + case 0x15: /* DAC data */ + case 0x16: + case 0x17: /* SHIFT */ + return; + case 0x18: /* I/O CTRL (Direction) */ + if(OPL->type&OPL_TYPE_IO) + OPL->portDirection = v&0x0f; + return; + case 0x19: /* I/O DATA */ + if(OPL->type&OPL_TYPE_IO) + { + OPL->portLatch = v; + if(OPL->porthandler_w) + OPL->porthandler_w(OPL->port_param,v&OPL->portDirection); + } + return; + case 0x1a: /* PCM data */ + return; +#endif +#endif + } + break; + case 0x20: /* am,vib,ksr,eg type,mul */ + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_mul(OPL,slot,v); + return; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_ksl_tl(OPL,slot,v); + return; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_ar_dr(OPL,slot,v); + return; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_sl_rr(OPL,slot,v); + return; + case 0xa0: + switch(r) + { + case 0xbd: + /* amsep,vibdep,r,bd,sd,tom,tc,hh */ + { + UINT8 rkey = OPL->rythm^v; + OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0]; + OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0]; + OPL->rythm = v&0x3f; + if(OPL->rythm&0x20) + { +#if 0 + usrintf_showmessage("OPL Rythm mode select"); +#endif + /* BD key on/off */ + if(rkey&0x10) + { + if(v&0x10) + { + OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0; + OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]); + OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]); + } + else + { + OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]); + OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]); + } + } + /* SD key on/off */ + if(rkey&0x08) + { + if(v&0x08) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]); + else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]); + }/* TAM key on/off */ + if(rkey&0x04) + { + if(v&0x04) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]); + else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]); + } + /* TOP-CY key on/off */ + if(rkey&0x02) + { + if(v&0x02) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]); + else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]); + } + /* HH key on/off */ + if(rkey&0x01) + { + if(v&0x01) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]); + else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]); + } + } + } + return; + } + /* keyon,block,fnum */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + if(!(r&0x10)) + { /* a0-a8 */ + block_fnum = (CH->block_fnum&0x1f00) | v; + } + else + { /* b0-b8 */ + int keyon = (v>>5)&1; + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + if(CH->keyon != keyon) + { + if( (CH->keyon=keyon) ) + { + CH->op1_out[0] = CH->op1_out[1] = 0; + OPL_KEYON(&CH->SLOT[SLOT1]); + OPL_KEYON(&CH->SLOT[SLOT2]); + } + else + { + OPL_KEYOFF(&CH->SLOT[SLOT1]); + OPL_KEYOFF(&CH->SLOT[SLOT2]); + } + } + } + /* update */ + if(CH->block_fnum != block_fnum) + { + int blockRv = 7-(block_fnum>>10); + int fnum = block_fnum&0x3ff; + CH->block_fnum = block_fnum; + + CH->ksl_base = KSL_TABLE[block_fnum>>6]; + CH->fc = OPL->FN_TABLE[fnum]>>blockRv; + CH->kcode = CH->block_fnum>>9; + if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1; + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + return; + case 0xc0: + /* FB,C */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + { + int feedback = (v>>1)&7; + CH->FB = feedback ? (8+1) - feedback : 0; + CH->CON = v&1; + set_algorythm(CH); + } + return; + case 0xe0: /* wave type */ + slot = slot_array[r&0x1f]; + if(slot == -1) return; + CH = &OPL->P_CH[slot/2]; + if(OPL->wavesel) + { + /* LOG(LOG_INF,("OPL SLOT %d wave select %d\n",slot,v&3)); */ + CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v&0x03)*SIN_ENT]; + } + return; + } +} + +/* lock/unlock for common table */ +static int OPL_LockTable(void) +{ + num_lock++; + if(num_lock>1) return 0; + /* first time */ + cur_chip = NULL; + /* allocate total level table (128kb space) */ + if( !OPLOpenTable() ) + { + num_lock--; + return -1; + } + return 0; +} + +static void OPL_UnLockTable(void) +{ + if(num_lock) num_lock--; + if(num_lock) return; + /* last time */ + cur_chip = NULL; + OPLCloseTable(); +} + +#if (BUILD_YM3812 || BUILD_YM3526) +/*******************************************************************************/ +/* YM3812 local section */ +/*******************************************************************************/ + +/* ---------- update one of chip ----------- */ +void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) +{ + int i; + int data; + OPLSAMPLE *buf = buffer; + UINT32 amsCnt = OPL->amsCnt; + UINT32 vibCnt = OPL->vibCnt; + UINT8 rythm = OPL->rythm&0x20; + OPL_CH *CH,*R_CH; + + if( (void *)OPL != cur_chip ){ + cur_chip = (void *)OPL; + /* channel pointers */ + S_CH = OPL->P_CH; + E_CH = &S_CH[9]; + /* rythm slot */ + SLOT7_1 = &S_CH[7].SLOT[SLOT1]; + SLOT7_2 = &S_CH[7].SLOT[SLOT2]; + SLOT8_1 = &S_CH[8].SLOT[SLOT1]; + SLOT8_2 = &S_CH[8].SLOT[SLOT2]; + /* LFO state */ + amsIncr = OPL->amsIncr; + vibIncr = OPL->vibIncr; + ams_table = OPL->ams_table; + vib_table = OPL->vib_table; + } + R_CH = rythm ? &S_CH[6] : E_CH; + for( i=0; i < length ; i++ ) + { + /* channel A channel B channel C */ + /* LFO */ + ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT]; + vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT]; + outd[0] = 0; + /* FM part */ + for(CH=S_CH ; CH < R_CH ; CH++) + OPL_CALC_CH(CH); + /* Rythn part */ + if(rythm) + OPL_CALC_RH(S_CH); + /* limit check */ + data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT ); + /* store to sound buffer */ + buf[i] = data >> OPL_OUTSB; + } + + OPL->amsCnt = amsCnt; + OPL->vibCnt = vibCnt; +#ifdef OPL_OUTPUT_LOG + if(opl_dbg_fp) + { + for(opl_dbg_chip=0;opl_dbg_chipamsCnt; + UINT32 vibCnt = OPL->vibCnt; + UINT8 rythm = OPL->rythm&0x20; + OPL_CH *CH,*R_CH; + YM_DELTAT *DELTAT = OPL->deltat; + + /* setup DELTA-T unit */ + YM_DELTAT_DECODE_PRESET(DELTAT); + + if( (void *)OPL != cur_chip ){ + cur_chip = (void *)OPL; + /* channel pointers */ + S_CH = OPL->P_CH; + E_CH = &S_CH[9]; + /* rythm slot */ + SLOT7_1 = &S_CH[7].SLOT[SLOT1]; + SLOT7_2 = &S_CH[7].SLOT[SLOT2]; + SLOT8_1 = &S_CH[8].SLOT[SLOT1]; + SLOT8_2 = &S_CH[8].SLOT[SLOT2]; + /* LFO state */ + amsIncr = OPL->amsIncr; + vibIncr = OPL->vibIncr; + ams_table = OPL->ams_table; + vib_table = OPL->vib_table; + } + R_CH = rythm ? &S_CH[6] : E_CH; + for( i=0; i < length ; i++ ) + { + /* channel A channel B channel C */ + /* LFO */ + ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT]; + vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT]; + outd[0] = 0; + /* deltaT ADPCM */ + if( DELTAT->portstate ) + YM_DELTAT_ADPCM_CALC(DELTAT); + /* FM part */ + for(CH=S_CH ; CH < R_CH ; CH++) + OPL_CALC_CH(CH); + /* Rythn part */ + if(rythm) + OPL_CALC_RH(S_CH); + /* limit check */ + data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT ); + /* store to sound buffer */ + buf[i] = data >> OPL_OUTSB; + } + OPL->amsCnt = amsCnt; + OPL->vibCnt = vibCnt; + /* deltaT START flag */ + if( !DELTAT->portstate ) + OPL->status &= 0xfe; +} +#endif + +/* ---------- reset one of chip ---------- */ +void OPLResetChip(FM_OPL *OPL) +{ + int c,s; + int i; + + /* reset chip */ + OPL->mode = 0; /* normal mode */ + OPL_STATUS_RESET(OPL,0x7f); + /* reset with register write */ + OPLWriteReg(OPL,0x01,0); /* wabesel disable */ + OPLWriteReg(OPL,0x02,0); /* Timer1 */ + OPLWriteReg(OPL,0x03,0); /* Timer2 */ + OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */ + for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); + /* reset OPerator paramater */ + for( c = 0 ; c < OPL->max_ch ; c++ ) + { + OPL_CH *CH = &OPL->P_CH[c]; + /* OPL->P_CH[c].PAN = OPN_CENTER; */ + for(s = 0 ; s < 2 ; s++ ) + { + /* wave table */ + CH->SLOT[s].wavetable = &SIN_TABLE[0]; + /* CH->SLOT[s].evm = ENV_MOD_RR; */ + CH->SLOT[s].evc = EG_OFF; + CH->SLOT[s].eve = EG_OFF+1; + CH->SLOT[s].evs = 0; + } + } +#if BUILD_Y8950 + if(OPL->type&OPL_TYPE_ADPCM) + { + YM_DELTAT *DELTAT = OPL->deltat; + + DELTAT->freqbase = OPL->freqbase; + DELTAT->output_pointer = outd; + DELTAT->portshift = 5; + DELTAT->output_range = DELTAT_MIXING_LEVEL<P_CH = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch; +#if BUILD_Y8950 + if(type&OPL_TYPE_ADPCM) OPL->deltat = (YM_DELTAT *)ptr; ptr+=sizeof(YM_DELTAT); +#endif + /* set channel state pointer */ + OPL->type = type; + OPL->clock = clock; + OPL->rate = rate; + OPL->max_ch = max_ch; + /* init grobal tables */ + OPL_initalize(OPL); + /* reset chip */ + OPLResetChip(OPL); +#ifdef OPL_OUTPUT_LOG + if(!opl_dbg_fp) + { + opl_dbg_fp = fopen("opllog.opl","wb"); + opl_dbg_maxchip = 0; + } + if(opl_dbg_fp) + { + opl_dbg_opl[opl_dbg_maxchip] = OPL; + fprintf(opl_dbg_fp,"%c%c%c%c%c%c",0x00+opl_dbg_maxchip, + type, + clock&0xff, + (clock/0x100)&0xff, + (clock/0x10000)&0xff, + (clock/0x1000000)&0xff); + opl_dbg_maxchip++; + } +#endif + return OPL; +} + +/* ---------- Destroy one of vietual YM3812 ---------- */ +void OPLDestroy(FM_OPL *OPL) +{ +#ifdef OPL_OUTPUT_LOG + if(opl_dbg_fp) + { + fclose(opl_dbg_fp); + opl_dbg_fp = NULL; + } +#endif + OPL_UnLockTable(); + free(OPL); +} + +/* ---------- Option handlers ---------- */ + +void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset) +{ + OPL->TimerHandler = TimerHandler; + OPL->TimerParam = channelOffset; +} +void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param) +{ + OPL->IRQHandler = IRQHandler; + OPL->IRQParam = param; +} +void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param) +{ + OPL->UpdateHandler = UpdateHandler; + OPL->UpdateParam = param; +} +#if BUILD_Y8950 +void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param) +{ + OPL->porthandler_w = PortHandler_w; + OPL->porthandler_r = PortHandler_r; + OPL->port_param = param; +} + +void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param) +{ + OPL->keyboardhandler_w = KeyboardHandler_w; + OPL->keyboardhandler_r = KeyboardHandler_r; + OPL->keyboard_param = param; +} +#endif +/* ---------- YM3812 I/O interface ---------- */ +int OPLWrite(FM_OPL *OPL,int a,int v) +{ + if( !(a&1) ) + { /* address port */ + OPL->address = v & 0xff; + } + else + { /* data port */ + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); +#ifdef OPL_OUTPUT_LOG + if(opl_dbg_fp) + { + for(opl_dbg_chip=0;opl_dbg_chipaddress,v); + } +#endif + OPLWriteReg(OPL,OPL->address,v); + } + return OPL->status>>7; +} + +unsigned char OPLRead(FM_OPL *OPL,int a) +{ + if( !(a&1) ) + { /* status port */ + return OPL->status & (OPL->statusmask|0x80); + } + /* data port */ + switch(OPL->address) + { + case 0x05: /* KeyBoard IN */ + if(OPL->type&OPL_TYPE_KEYBOARD) + { + if(OPL->keyboardhandler_r) + return OPL->keyboardhandler_r(OPL->keyboard_param); + else + LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n")); + } + return 0; +#if 0 + case 0x0f: /* ADPCM-DATA */ + return 0; +#endif + case 0x19: /* I/O DATA */ + if(OPL->type&OPL_TYPE_IO) + { + if(OPL->porthandler_r) + return OPL->porthandler_r(OPL->port_param); + else + LOG(LOG_WAR,("OPL:read unmapped I/O port\n")); + } + return 0; + case 0x1a: /* PCM-DATA */ + return 0; + } + return 0; +} + +int OPLTimerOver(FM_OPL *OPL,int c) +{ + if( c ) + { /* Timer B */ + OPL_STATUS_SET(OPL,0x20); + } + else + { /* Timer A */ + OPL_STATUS_SET(OPL,0x40); + /* CSM mode key,TL controll */ + if( OPL->mode & 0x80 ) + { /* CSM mode total level latch and auto key on */ + int ch; + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); + for(ch=0;ch<9;ch++) + CSMKeyControll( &OPL->P_CH[ch] ); + } + } + /* reload timer */ + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase); + return OPL->status>>7; +} diff --git a/tools/ioemu/hw/fmopl.h b/tools/ioemu/hw/fmopl.h new file mode 100644 index 0000000000..a01ff902c7 --- /dev/null +++ b/tools/ioemu/hw/fmopl.h @@ -0,0 +1,174 @@ +#ifndef __FMOPL_H_ +#define __FMOPL_H_ + +/* --- select emulation chips --- */ +#define BUILD_YM3812 (HAS_YM3812) +//#define BUILD_YM3526 (HAS_YM3526) +//#define BUILD_Y8950 (HAS_Y8950) + +/* --- system optimize --- */ +/* select bit size of output : 8 or 16 */ +#define OPL_OUTPUT_BIT 16 + +/* compiler dependence */ +#ifndef OSD_CPU_H +#define OSD_CPU_H +typedef unsigned char UINT8; /* unsigned 8bit */ +typedef unsigned short UINT16; /* unsigned 16bit */ +typedef unsigned int UINT32; /* unsigned 32bit */ +typedef signed char INT8; /* signed 8bit */ +typedef signed short INT16; /* signed 16bit */ +typedef signed int INT32; /* signed 32bit */ +#endif + +#if (OPL_OUTPUT_BIT==16) +typedef INT16 OPLSAMPLE; +#endif +#if (OPL_OUTPUT_BIT==8) +typedef unsigned char OPLSAMPLE; +#endif + + +#if BUILD_Y8950 +#include "ymdeltat.h" +#endif + +typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec); +typedef void (*OPL_IRQHANDLER)(int param,int irq); +typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us); +typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data); +typedef unsigned char (*OPL_PORTHANDLER_R)(int param); + +/* !!!!! here is private section , do not access there member direct !!!!! */ + +#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ +#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */ +#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */ +#define OPL_TYPE_IO 0x08 /* I/O port */ + +/* Saving is necessary for member of the 'R' mark for suspend/resume */ +/* ---------- OPL one of slot ---------- */ +typedef struct fm_opl_slot { + INT32 TL; /* total level :TL << 8 */ + INT32 TLL; /* adjusted now TL */ + UINT8 KSR; /* key scale rate :(shift down bit) */ + INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */ + INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */ + INT32 SL; /* sustin level :SL_TALBE[SL] */ + INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */ + UINT8 ksl; /* keyscale level :(shift down bits) */ + UINT8 ksr; /* key scale rate :kcode>>KSR */ + UINT32 mul; /* multiple :ML_TABLE[ML] */ + UINT32 Cnt; /* frequency count : */ + UINT32 Incr; /* frequency step : */ + /* envelope generator state */ + UINT8 eg_typ; /* envelope type flag */ + UINT8 evm; /* envelope phase */ + INT32 evc; /* envelope counter */ + INT32 eve; /* envelope counter end point */ + INT32 evs; /* envelope counter step */ + INT32 evsa; /* envelope step for AR :AR[ksr] */ + INT32 evsd; /* envelope step for DR :DR[ksr] */ + INT32 evsr; /* envelope step for RR :RR[ksr] */ + /* LFO */ + UINT8 ams; /* ams flag */ + UINT8 vib; /* vibrate flag */ + /* wave selector */ + INT32 **wavetable; +}OPL_SLOT; + +/* ---------- OPL one of channel ---------- */ +typedef struct fm_opl_channel { + OPL_SLOT SLOT[2]; + UINT8 CON; /* connection type */ + UINT8 FB; /* feed back :(shift down bit) */ + INT32 *connect1; /* slot1 output pointer */ + INT32 *connect2; /* slot2 output pointer */ + INT32 op1_out[2]; /* slot1 output for selfeedback */ + /* phase generator state */ + UINT32 block_fnum; /* block+fnum : */ + UINT8 kcode; /* key code : KeyScaleCode */ + UINT32 fc; /* Freq. Increment base */ + UINT32 ksl_base; /* KeyScaleLevel Base step */ + UINT8 keyon; /* key on/off flag */ +} OPL_CH; + +/* OPL state */ +typedef struct fm_opl_f { + UINT8 type; /* chip type */ + int clock; /* master clock (Hz) */ + int rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + double TimerBase; /* Timer base time (==sampling time) */ + UINT8 address; /* address register */ + UINT8 status; /* status flag */ + UINT8 statusmask; /* status mask */ + UINT32 mode; /* Reg.08 : CSM , notesel,etc. */ + /* Timer */ + int T[2]; /* timer counter */ + UINT8 st[2]; /* timer enable */ + /* FM channel slots */ + OPL_CH *P_CH; /* pointer of CH */ + int max_ch; /* maximum channel */ + /* Rythm sention */ + UINT8 rythm; /* Rythm mode , key flag */ +#if BUILD_Y8950 + /* Delta-T ADPCM unit (Y8950) */ + YM_DELTAT *deltat; /* DELTA-T ADPCM */ +#endif + /* Keyboard / I/O interface unit (Y8950) */ + UINT8 portDirection; + UINT8 portLatch; + OPL_PORTHANDLER_R porthandler_r; + OPL_PORTHANDLER_W porthandler_w; + int port_param; + OPL_PORTHANDLER_R keyboardhandler_r; + OPL_PORTHANDLER_W keyboardhandler_w; + int keyboard_param; + /* time tables */ + INT32 AR_TABLE[75]; /* atttack rate tables */ + INT32 DR_TABLE[75]; /* decay rate tables */ + UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */ + /* LFO */ + INT32 *ams_table; + INT32 *vib_table; + INT32 amsCnt; + INT32 amsIncr; + INT32 vibCnt; + INT32 vibIncr; + /* wave selector enable flag */ + UINT8 wavesel; + /* external event callback handler */ + OPL_TIMERHANDLER TimerHandler; /* TIMER handler */ + int TimerParam; /* TIMER parameter */ + OPL_IRQHANDLER IRQHandler; /* IRQ handler */ + int IRQParam; /* IRQ parameter */ + OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */ + int UpdateParam; /* stream update parameter */ +} FM_OPL; + +/* ---------- Generic interface section ---------- */ +#define OPL_TYPE_YM3526 (0) +#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) +#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO) + +FM_OPL *OPLCreate(int type, int clock, int rate); +void OPLDestroy(FM_OPL *OPL); +void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset); +void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param); +void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param); +/* Y8950 port handlers */ +void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param); +void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param); + +void OPLResetChip(FM_OPL *OPL); +int OPLWrite(FM_OPL *OPL,int a,int v); +unsigned char OPLRead(FM_OPL *OPL,int a); +int OPLTimerOver(FM_OPL *OPL,int c); + +/* YM3626/YM3812 local section */ +void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length); + +void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length); + +#endif diff --git a/tools/ioemu/hw/heathrow_pic.c b/tools/ioemu/hw/heathrow_pic.c new file mode 100644 index 0000000000..4980cef467 --- /dev/null +++ b/tools/ioemu/hw/heathrow_pic.c @@ -0,0 +1,168 @@ +/* + * Heathrow PIC support (standard PowerMac PIC) + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG + +typedef struct HeathrowPIC { + uint32_t events; + uint32_t mask; + uint32_t levels; + uint32_t level_triggered; +} HeathrowPIC; + +struct HeathrowPICS { + HeathrowPIC pics[2]; +}; + +static inline int check_irq(HeathrowPIC *pic) +{ + return (pic->events | (pic->levels & pic->level_triggered)) & pic->mask; +} + +/* update the CPU irq state */ +static void heathrow_pic_update(HeathrowPICS *s) +{ + if (check_irq(&s->pics[0]) || check_irq(&s->pics[1])) { + cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD); + } +} + +static void pic_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + HeathrowPICS *s = opaque; + HeathrowPIC *pic; + unsigned int n; + + value = bswap32(value); +#ifdef DEBUG + printf("pic_writel: %08x: %08x\n", + addr, value); +#endif + n = ((addr & 0xfff) - 0x10) >> 4; + if (n >= 2) + return; + pic = &s->pics[n]; + switch(addr & 0xf) { + case 0x04: + pic->mask = value; + heathrow_pic_update(s); + break; + case 0x08: + /* do not reset level triggered IRQs */ + value &= ~pic->level_triggered; + pic->events &= ~value; + heathrow_pic_update(s); + break; + default: + break; + } +} + +static uint32_t pic_readl (void *opaque, target_phys_addr_t addr) +{ + HeathrowPICS *s = opaque; + HeathrowPIC *pic; + unsigned int n; + uint32_t value; + + n = ((addr & 0xfff) - 0x10) >> 4; + if (n >= 2) { + value = 0; + } else { + pic = &s->pics[n]; + switch(addr & 0xf) { + case 0x0: + value = pic->events; + break; + case 0x4: + value = pic->mask; + break; + case 0xc: + value = pic->levels; + break; + default: + value = 0; + break; + } + } +#ifdef DEBUG + printf("pic_readl: %08x: %08x\n", + addr, value); +#endif + value = bswap32(value); + return value; +} + +static CPUWriteMemoryFunc *pic_write[] = { + &pic_writel, + &pic_writel, + &pic_writel, +}; + +static CPUReadMemoryFunc *pic_read[] = { + &pic_readl, + &pic_readl, + &pic_readl, +}; + + +void heathrow_pic_set_irq(void *opaque, int num, int level) +{ + HeathrowPICS *s = opaque; + HeathrowPIC *pic; + unsigned int irq_bit; + +#if defined(DEBUG) + { + static int last_level[64]; + if (last_level[num] != level) { + printf("set_irq: num=0x%02x level=%d\n", num, level); + last_level[num] = level; + } + } +#endif + pic = &s->pics[1 - (num >> 5)]; + irq_bit = 1 << (num & 0x1f); + if (level) { + pic->events |= irq_bit & ~pic->level_triggered; + pic->levels |= irq_bit; + } else { + pic->levels &= ~irq_bit; + } + heathrow_pic_update(s); +} + +HeathrowPICS *heathrow_pic_init(int *pmem_index) +{ + HeathrowPICS *s; + + s = qemu_mallocz(sizeof(HeathrowPICS)); + s->pics[0].level_triggered = 0; + s->pics[1].level_triggered = 0x1ff00000; + *pmem_index = cpu_register_io_memory(0, pic_read, pic_write, s); + return s; +} diff --git a/tools/ioemu/hw/i8254.c b/tools/ioemu/hw/i8254.c new file mode 100644 index 0000000000..a4097632eb --- /dev/null +++ b/tools/ioemu/hw/i8254.c @@ -0,0 +1,482 @@ +/* + * QEMU 8253/8254 interval timer emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG_PIT + +#define RW_STATE_LSB 1 +#define RW_STATE_MSB 2 +#define RW_STATE_WORD0 3 +#define RW_STATE_WORD1 4 + +typedef struct PITChannelState { + int count; /* can be 65536 */ + uint16_t latched_count; + uint8_t count_latched; + uint8_t status_latched; + uint8_t status; + uint8_t read_state; + uint8_t write_state; + uint8_t write_latch; + uint8_t rw_mode; + uint8_t mode; + uint8_t bcd; /* not supported */ + uint8_t gate; /* timer start */ + int64_t count_load_time; + /* irq handling */ + int64_t next_transition_time; + QEMUTimer *irq_timer; + int irq; +} PITChannelState; + +struct PITState { + PITChannelState channels[3]; +}; + +static PITState pit_state; + +static void pit_irq_timer_update(PITChannelState *s, int64_t current_time); + +static int pit_get_count(PITChannelState *s) +{ + uint64_t d; + int counter; + + d = muldiv64(qemu_get_clock(vm_clock) - s->count_load_time, PIT_FREQ, ticks_per_sec); + switch(s->mode) { + case 0: + case 1: + case 4: + case 5: + counter = (s->count - d) & 0xffff; + break; + case 3: + /* XXX: may be incorrect for odd counts */ + counter = s->count - ((2 * d) % s->count); + break; + default: + counter = s->count - (d % s->count); + break; + } + return counter; +} + +/* get pit output bit */ +static int pit_get_out1(PITChannelState *s, int64_t current_time) +{ + uint64_t d; + int out; + + d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec); + switch(s->mode) { + default: + case 0: + out = (d >= s->count); + break; + case 1: + out = (d < s->count); + break; + case 2: + if ((d % s->count) == 0 && d != 0) + out = 1; + else + out = 0; + break; + case 3: + out = (d % s->count) < ((s->count + 1) >> 1); + break; + case 4: + case 5: + out = (d == s->count); + break; + } + return out; +} + +int pit_get_out(PITState *pit, int channel, int64_t current_time) +{ + PITChannelState *s = &pit->channels[channel]; + return pit_get_out1(s, current_time); +} + +/* return -1 if no transition will occur. */ +static int64_t pit_get_next_transition_time(PITChannelState *s, + int64_t current_time) +{ + uint64_t d, next_time, base; + int period2; + + d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec); + switch(s->mode) { + default: + case 0: + case 1: + if (d < s->count) + next_time = s->count; + else + return -1; + break; + case 2: + base = (d / s->count) * s->count; + if ((d - base) == 0 && d != 0) + next_time = base + s->count; + else + next_time = base + s->count + 1; + break; + case 3: + base = (d / s->count) * s->count; + period2 = ((s->count + 1) >> 1); + if ((d - base) < period2) + next_time = base + period2; + else + next_time = base + s->count; + break; + case 4: + case 5: + if (d < s->count) + next_time = s->count; + else if (d == s->count) + next_time = s->count + 1; + else + return -1; + break; + } + /* convert to timer units */ + next_time = s->count_load_time + muldiv64(next_time, ticks_per_sec, PIT_FREQ); + /* fix potential rounding problems */ + /* XXX: better solution: use a clock at PIT_FREQ Hz */ + if (next_time <= current_time) + next_time = current_time + 1; + return next_time; +} + +/* val must be 0 or 1 */ +void pit_set_gate(PITState *pit, int channel, int val) +{ + PITChannelState *s = &pit->channels[channel]; + + switch(s->mode) { + default: + case 0: + case 4: + /* XXX: just disable/enable counting */ + break; + case 1: + case 5: + if (s->gate < val) { + /* restart counting on rising edge */ + s->count_load_time = qemu_get_clock(vm_clock); + pit_irq_timer_update(s, s->count_load_time); + } + break; + case 2: + case 3: + if (s->gate < val) { + /* restart counting on rising edge */ + s->count_load_time = qemu_get_clock(vm_clock); + pit_irq_timer_update(s, s->count_load_time); + } + /* XXX: disable/enable counting */ + break; + } + s->gate = val; +} + +int pit_get_gate(PITState *pit, int channel) +{ + PITChannelState *s = &pit->channels[channel]; + return s->gate; +} + +int pit_get_initial_count(PITState *pit, int channel) +{ + PITChannelState *s = &pit->channels[channel]; + return s->count; +} + +int pit_get_mode(PITState *pit, int channel) +{ + PITChannelState *s = &pit->channels[channel]; + return s->mode; +} + +static inline void pit_load_count(PITChannelState *s, int val) +{ + if (val == 0) + val = 0x10000; + s->count_load_time = qemu_get_clock(vm_clock); + s->count = val; + pit_irq_timer_update(s, s->count_load_time); +} + +/* if already latched, do not latch again */ +static void pit_latch_count(PITChannelState *s) +{ + if (!s->count_latched) { + s->latched_count = pit_get_count(s); + s->count_latched = s->rw_mode; + } +} + +static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + PITState *pit = opaque; + int channel, access; + PITChannelState *s; + + addr &= 3; + if (addr == 3) { + channel = val >> 6; + if (channel == 3) { + /* read back command */ + for(channel = 0; channel < 3; channel++) { + s = &pit->channels[channel]; + if (val & (2 << channel)) { + if (!(val & 0x20)) { + pit_latch_count(s); + } + if (!(val & 0x10) && !s->status_latched) { + /* status latch */ + /* XXX: add BCD and null count */ + s->status = (pit_get_out1(s, qemu_get_clock(vm_clock)) << 7) | + (s->rw_mode << 4) | + (s->mode << 1) | + s->bcd; + s->status_latched = 1; + } + } + } + } else { + s = &pit->channels[channel]; + access = (val >> 4) & 3; + if (access == 0) { + pit_latch_count(s); + } else { + s->rw_mode = access; + s->read_state = access; + s->write_state = access; + + s->mode = (val >> 1) & 7; + s->bcd = val & 1; + /* XXX: update irq timer ? */ + } + } + } else { + s = &pit->channels[addr]; + switch(s->write_state) { + default: + case RW_STATE_LSB: + pit_load_count(s, val); + break; + case RW_STATE_MSB: + pit_load_count(s, val << 8); + break; + case RW_STATE_WORD0: + s->write_latch = val; + s->write_state = RW_STATE_WORD1; + break; + case RW_STATE_WORD1: + pit_load_count(s, s->write_latch | (val << 8)); + s->write_state = RW_STATE_WORD0; + break; + } + } +} + +static uint32_t pit_ioport_read(void *opaque, uint32_t addr) +{ + PITState *pit = opaque; + int ret, count; + PITChannelState *s; + + addr &= 3; + s = &pit->channels[addr]; + if (s->status_latched) { + s->status_latched = 0; + ret = s->status; + } else if (s->count_latched) { + switch(s->count_latched) { + default: + case RW_STATE_LSB: + ret = s->latched_count & 0xff; + s->count_latched = 0; + break; + case RW_STATE_MSB: + ret = s->latched_count >> 8; + s->count_latched = 0; + break; + case RW_STATE_WORD0: + ret = s->latched_count & 0xff; + s->count_latched = RW_STATE_MSB; + break; + } + } else { + switch(s->read_state) { + default: + case RW_STATE_LSB: + count = pit_get_count(s); + ret = count & 0xff; + break; + case RW_STATE_MSB: + count = pit_get_count(s); + ret = (count >> 8) & 0xff; + break; + case RW_STATE_WORD0: + count = pit_get_count(s); + ret = count & 0xff; + s->read_state = RW_STATE_WORD1; + break; + case RW_STATE_WORD1: + count = pit_get_count(s); + ret = (count >> 8) & 0xff; + s->read_state = RW_STATE_WORD0; + break; + } + } + return ret; +} + +static void pit_irq_timer_update(PITChannelState *s, int64_t current_time) +{ + int64_t expire_time; + int irq_level; + + if (!s->irq_timer) + return; + expire_time = pit_get_next_transition_time(s, current_time); + irq_level = pit_get_out1(s, current_time); + pic_set_irq(s->irq, irq_level); +#ifdef DEBUG_PIT + printf("irq_level=%d next_delay=%f\n", + irq_level, + (double)(expire_time - current_time) / ticks_per_sec); +#endif + s->next_transition_time = expire_time; + if (expire_time != -1) + qemu_mod_timer(s->irq_timer, expire_time); + else + qemu_del_timer(s->irq_timer); +} + +static void pit_irq_timer(void *opaque) +{ + PITChannelState *s = opaque; + + pit_irq_timer_update(s, s->next_transition_time); +} + +static void pit_save(QEMUFile *f, void *opaque) +{ + PITState *pit = opaque; + PITChannelState *s; + int i; + + for(i = 0; i < 3; i++) { + s = &pit->channels[i]; + qemu_put_be32s(f, &s->count); + qemu_put_be16s(f, &s->latched_count); + qemu_put_8s(f, &s->count_latched); + qemu_put_8s(f, &s->status_latched); + qemu_put_8s(f, &s->status); + qemu_put_8s(f, &s->read_state); + qemu_put_8s(f, &s->write_state); + qemu_put_8s(f, &s->write_latch); + qemu_put_8s(f, &s->rw_mode); + qemu_put_8s(f, &s->mode); + qemu_put_8s(f, &s->bcd); + qemu_put_8s(f, &s->gate); + qemu_put_be64s(f, &s->count_load_time); + if (s->irq_timer) { + qemu_put_be64s(f, &s->next_transition_time); + qemu_put_timer(f, s->irq_timer); + } + } +} + +static int pit_load(QEMUFile *f, void *opaque, int version_id) +{ + PITState *pit = opaque; + PITChannelState *s; + int i; + + if (version_id != 1) + return -EINVAL; + + for(i = 0; i < 3; i++) { + s = &pit->channels[i]; + qemu_get_be32s(f, &s->count); + qemu_get_be16s(f, &s->latched_count); + qemu_get_8s(f, &s->count_latched); + qemu_get_8s(f, &s->status_latched); + qemu_get_8s(f, &s->status); + qemu_get_8s(f, &s->read_state); + qemu_get_8s(f, &s->write_state); + qemu_get_8s(f, &s->write_latch); + qemu_get_8s(f, &s->rw_mode); + qemu_get_8s(f, &s->mode); + qemu_get_8s(f, &s->bcd); + qemu_get_8s(f, &s->gate); + qemu_get_be64s(f, &s->count_load_time); + if (s->irq_timer) { + qemu_get_be64s(f, &s->next_transition_time); + qemu_get_timer(f, s->irq_timer); + } + } + return 0; +} + +static void pit_reset(void *opaque) +{ + PITState *pit = opaque; + PITChannelState *s; + int i; + + for(i = 0;i < 3; i++) { + s = &pit->channels[i]; + s->mode = 3; + s->gate = (i != 2); + pit_load_count(s, 0); + } +} + +PITState *pit_init(int base, int irq) +{ + PITState *pit = &pit_state; + PITChannelState *s; + + s = &pit->channels[0]; + /* the timer 0 is connected to an IRQ */ + s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s); + s->irq = irq; + + register_savevm("i8254", base, 1, pit_save, pit_load, pit); + + qemu_register_reset(pit_reset, pit); + register_ioport_write(base, 4, 1, pit_ioport_write, pit); + register_ioport_read(base, 3, 1, pit_ioport_read, pit); + + pit_reset(pit); + + return pit; +} diff --git a/tools/ioemu/hw/i8259.c b/tools/ioemu/hw/i8259.c new file mode 100644 index 0000000000..6c2ddfff8c --- /dev/null +++ b/tools/ioemu/hw/i8259.c @@ -0,0 +1,561 @@ +/* + * QEMU 8259 interrupt controller emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* debug PIC */ +//#define DEBUG_PIC + +//#define DEBUG_IRQ_LATENCY +//#define DEBUG_IRQ_COUNT + +typedef struct PicState { + uint8_t last_irr; /* edge detection */ + uint8_t irr; /* interrupt request register */ + uint8_t imr; /* interrupt mask register */ + uint8_t isr; /* interrupt service register */ + uint8_t priority_add; /* highest irq priority */ + uint8_t irq_base; + uint8_t read_reg_select; + uint8_t poll; + uint8_t special_mask; + uint8_t init_state; + uint8_t auto_eoi; + uint8_t rotate_on_auto_eoi; + uint8_t special_fully_nested_mode; + uint8_t init4; /* true if 4 byte init */ + uint8_t elcr; /* PIIX edge/trigger selection*/ + uint8_t elcr_mask; + PicState2 *pics_state; +} PicState; + +struct PicState2 { + /* 0 is master pic, 1 is slave pic */ + /* XXX: better separation between the two pics */ + PicState pics[2]; + IRQRequestFunc *irq_request; + void *irq_request_opaque; + /* IOAPIC callback support */ + SetIRQFunc *alt_irq_func; + void *alt_irq_opaque; +}; + +#if defined(DEBUG_PIC) || defined (DEBUG_IRQ_COUNT) +static int irq_level[16]; +#endif +#ifdef DEBUG_IRQ_COUNT +static uint64_t irq_count[16]; +#endif + +/* set irq level. If an edge is detected, then the IRR is set to 1 */ +static inline void pic_set_irq1(PicState *s, int irq, int level) +{ + int mask; + mask = 1 << irq; + if (s->elcr & mask) { + /* level triggered */ + if (level) { + s->irr |= mask; + s->last_irr |= mask; + } else { + s->irr &= ~mask; + s->last_irr &= ~mask; + } + } else { + /* edge triggered */ + if (level) { + if ((s->last_irr & mask) == 0) + s->irr |= mask; + s->last_irr |= mask; + } else { + s->last_irr &= ~mask; + } + } +} + +/* return the highest priority found in mask (highest = smallest + number). Return 8 if no irq */ +static inline int get_priority(PicState *s, int mask) +{ + int priority; + if (mask == 0) + return 8; + priority = 0; + while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) + priority++; + return priority; +} + +/* return the pic wanted interrupt. return -1 if none */ +static int pic_get_irq(PicState *s) +{ + int mask, cur_priority, priority; + + mask = s->irr & ~s->imr; + priority = get_priority(s, mask); + if (priority == 8) + return -1; + /* compute current priority. If special fully nested mode on the + master, the IRQ coming from the slave is not taken into account + for the priority computation. */ + mask = s->isr; + if (s->special_fully_nested_mode && s == &s->pics_state->pics[0]) + mask &= ~(1 << 2); + cur_priority = get_priority(s, mask); + if (priority < cur_priority) { + /* higher priority found: an irq should be generated */ + return (priority + s->priority_add) & 7; + } else { + return -1; + } +} + +/* raise irq to CPU if necessary. must be called every time the active + irq may change */ +/* XXX: should not export it, but it is needed for an APIC kludge */ +void pic_update_irq(PicState2 *s) +{ + int irq2, irq; + + /* first look at slave pic */ + irq2 = pic_get_irq(&s->pics[1]); + if (irq2 >= 0) { + /* if irq request by slave pic, signal master PIC */ + pic_set_irq1(&s->pics[0], 2, 1); + pic_set_irq1(&s->pics[0], 2, 0); + } + /* look at requested irq */ + irq = pic_get_irq(&s->pics[0]); + if (irq >= 0) { +#if defined(DEBUG_PIC) + { + int i; + for(i = 0; i < 2; i++) { + printf("pic%d: imr=%x irr=%x padd=%d\n", + i, s->pics[i].imr, s->pics[i].irr, + s->pics[i].priority_add); + + } + } + printf("pic: cpu_interrupt\n"); +#endif + s->irq_request(s->irq_request_opaque, 1); + } +} + +#ifdef DEBUG_IRQ_LATENCY +int64_t irq_time[16]; +#endif + +void pic_set_irq_new(void *opaque, int irq, int level) +{ + PicState2 *s = opaque; + +#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) + if (level != irq_level[irq]) { +#if defined(DEBUG_PIC) + printf("pic_set_irq: irq=%d level=%d\n", irq, level); +#endif + irq_level[irq] = level; +#ifdef DEBUG_IRQ_COUNT + if (level == 1) + irq_count[irq]++; +#endif + } +#endif +#ifdef DEBUG_IRQ_LATENCY + if (level) { + irq_time[irq] = qemu_get_clock(vm_clock); + } +#endif + pic_set_irq1(&s->pics[irq >> 3], irq & 7, level); + /* used for IOAPIC irqs */ + if (s->alt_irq_func) + s->alt_irq_func(s->alt_irq_opaque, irq, level); + pic_update_irq(s); +} + +/* obsolete function */ +void pic_set_irq(int irq, int level) +{ + pic_set_irq_new(isa_pic, irq, level); +} + +/* acknowledge interrupt 'irq' */ +static inline void pic_intack(PicState *s, int irq) +{ + if (s->auto_eoi) { + if (s->rotate_on_auto_eoi) + s->priority_add = (irq + 1) & 7; + } else { + s->isr |= (1 << irq); + } + /* We don't clear a level sensitive interrupt here */ + if (!(s->elcr & (1 << irq))) + s->irr &= ~(1 << irq); +} + +int pic_read_irq(PicState2 *s) +{ + int irq, irq2, intno; + + irq = pic_get_irq(&s->pics[0]); + if (irq >= 0) { + pic_intack(&s->pics[0], irq); + if (irq == 2) { + irq2 = pic_get_irq(&s->pics[1]); + if (irq2 >= 0) { + pic_intack(&s->pics[1], irq2); + } else { + /* spurious IRQ on slave controller */ + irq2 = 7; + } + intno = s->pics[1].irq_base + irq2; + irq = irq2 + 8; + } else { + intno = s->pics[0].irq_base + irq; + } + } else { + /* spurious IRQ on host controller */ + irq = 7; + intno = s->pics[0].irq_base + irq; + } + pic_update_irq(s); + +#ifdef DEBUG_IRQ_LATENCY + printf("IRQ%d latency=%0.3fus\n", + irq, + (double)(qemu_get_clock(vm_clock) - irq_time[irq]) * 1000000.0 / ticks_per_sec); +#endif +#if defined(DEBUG_PIC) + printf("pic_interrupt: irq=%d\n", irq); +#endif + return intno; +} + +static void pic_reset(void *opaque) +{ + PicState *s = opaque; + + s->last_irr = 0; + s->irr = 0; + s->imr = 0; + s->isr = 0; + s->priority_add = 0; + s->irq_base = 0; + s->read_reg_select = 0; + s->poll = 0; + s->special_mask = 0; + s->init_state = 0; + s->auto_eoi = 0; + s->rotate_on_auto_eoi = 0; + s->special_fully_nested_mode = 0; + s->init4 = 0; + /* Note: ELCR is not reset */ +} + +static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + PicState *s = opaque; + int priority, cmd, irq; + +#ifdef DEBUG_PIC + printf("pic_write: addr=0x%02x val=0x%02x\n", addr, val); +#endif + addr &= 1; + if (addr == 0) { + if (val & 0x10) { + /* init */ + pic_reset(s); + /* deassert a pending interrupt */ + s->pics_state->irq_request(s->pics_state->irq_request_opaque, 0); + s->init_state = 1; + s->init4 = val & 1; + if (val & 0x02) + hw_error("single mode not supported"); + if (val & 0x08) + hw_error("level sensitive irq not supported"); + } else if (val & 0x08) { + if (val & 0x04) + s->poll = 1; + if (val & 0x02) + s->read_reg_select = val & 1; + if (val & 0x40) + s->special_mask = (val >> 5) & 1; + } else { + cmd = val >> 5; + switch(cmd) { + case 0: + case 4: + s->rotate_on_auto_eoi = cmd >> 2; + break; + case 1: /* end of interrupt */ + case 5: + priority = get_priority(s, s->isr); + if (priority != 8) { + irq = (priority + s->priority_add) & 7; + s->isr &= ~(1 << irq); + if (cmd == 5) + s->priority_add = (irq + 1) & 7; + pic_update_irq(s->pics_state); + } + break; + case 3: + irq = val & 7; + s->isr &= ~(1 << irq); + pic_update_irq(s->pics_state); + break; + case 6: + s->priority_add = (val + 1) & 7; + pic_update_irq(s->pics_state); + break; + case 7: + irq = val & 7; + s->isr &= ~(1 << irq); + s->priority_add = (irq + 1) & 7; + pic_update_irq(s->pics_state); + break; + default: + /* no operation */ + break; + } + } + } else { + switch(s->init_state) { + case 0: + /* normal mode */ + s->imr = val; + pic_update_irq(s->pics_state); + break; + case 1: + s->irq_base = val & 0xf8; + s->init_state = 2; + break; + case 2: + if (s->init4) { + s->init_state = 3; + } else { + s->init_state = 0; + } + break; + case 3: + s->special_fully_nested_mode = (val >> 4) & 1; + s->auto_eoi = (val >> 1) & 1; + s->init_state = 0; + break; + } + } +} + +static uint32_t pic_poll_read (PicState *s, uint32_t addr1) +{ + int ret; + + ret = pic_get_irq(s); + if (ret >= 0) { + if (addr1 >> 7) { + s->pics_state->pics[0].isr &= ~(1 << 2); + s->pics_state->pics[0].irr &= ~(1 << 2); + } + s->irr &= ~(1 << ret); + s->isr &= ~(1 << ret); + if (addr1 >> 7 || ret != 2) + pic_update_irq(s->pics_state); + } else { + ret = 0x07; + pic_update_irq(s->pics_state); + } + + return ret; +} + +static uint32_t pic_ioport_read(void *opaque, uint32_t addr1) +{ + PicState *s = opaque; + unsigned int addr; + int ret; + + addr = addr1; + addr &= 1; + if (s->poll) { + ret = pic_poll_read(s, addr1); + s->poll = 0; + } else { + if (addr == 0) { + if (s->read_reg_select) + ret = s->isr; + else + ret = s->irr; + } else { + ret = s->imr; + } + } +#ifdef DEBUG_PIC + printf("pic_read: addr=0x%02x val=0x%02x\n", addr1, ret); +#endif + return ret; +} + +/* memory mapped interrupt status */ +/* XXX: may be the same than pic_read_irq() */ +uint32_t pic_intack_read(PicState2 *s) +{ + int ret; + + ret = pic_poll_read(&s->pics[0], 0x00); + if (ret == 2) + ret = pic_poll_read(&s->pics[1], 0x80) + 8; + /* Prepare for ISR read */ + s->pics[0].read_reg_select = 1; + + return ret; +} + +static void elcr_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + PicState *s = opaque; + s->elcr = val & s->elcr_mask; +} + +static uint32_t elcr_ioport_read(void *opaque, uint32_t addr1) +{ + PicState *s = opaque; + return s->elcr; +} + +static void pic_save(QEMUFile *f, void *opaque) +{ + PicState *s = opaque; + + qemu_put_8s(f, &s->last_irr); + qemu_put_8s(f, &s->irr); + qemu_put_8s(f, &s->imr); + qemu_put_8s(f, &s->isr); + qemu_put_8s(f, &s->priority_add); + qemu_put_8s(f, &s->irq_base); + qemu_put_8s(f, &s->read_reg_select); + qemu_put_8s(f, &s->poll); + qemu_put_8s(f, &s->special_mask); + qemu_put_8s(f, &s->init_state); + qemu_put_8s(f, &s->auto_eoi); + qemu_put_8s(f, &s->rotate_on_auto_eoi); + qemu_put_8s(f, &s->special_fully_nested_mode); + qemu_put_8s(f, &s->init4); + qemu_put_8s(f, &s->elcr); +} + +static int pic_load(QEMUFile *f, void *opaque, int version_id) +{ + PicState *s = opaque; + + if (version_id != 1) + return -EINVAL; + + qemu_get_8s(f, &s->last_irr); + qemu_get_8s(f, &s->irr); + qemu_get_8s(f, &s->imr); + qemu_get_8s(f, &s->isr); + qemu_get_8s(f, &s->priority_add); + qemu_get_8s(f, &s->irq_base); + qemu_get_8s(f, &s->read_reg_select); + qemu_get_8s(f, &s->poll); + qemu_get_8s(f, &s->special_mask); + qemu_get_8s(f, &s->init_state); + qemu_get_8s(f, &s->auto_eoi); + qemu_get_8s(f, &s->rotate_on_auto_eoi); + qemu_get_8s(f, &s->special_fully_nested_mode); + qemu_get_8s(f, &s->init4); + qemu_get_8s(f, &s->elcr); + return 0; +} + +/* XXX: add generic master/slave system */ +static void pic_init1(int io_addr, int elcr_addr, PicState *s) +{ + register_ioport_write(io_addr, 2, 1, pic_ioport_write, s); + register_ioport_read(io_addr, 2, 1, pic_ioport_read, s); + if (elcr_addr >= 0) { + register_ioport_write(elcr_addr, 1, 1, elcr_ioport_write, s); + register_ioport_read(elcr_addr, 1, 1, elcr_ioport_read, s); + } + register_savevm("i8259", io_addr, 1, pic_save, pic_load, s); + qemu_register_reset(pic_reset, s); +} + +void pic_info(void) +{ + int i; + PicState *s; + + if (!isa_pic) + return; + + for(i=0;i<2;i++) { + s = &isa_pic->pics[i]; + term_printf("pic%d: irr=%02x imr=%02x isr=%02x hprio=%d irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n", + i, s->irr, s->imr, s->isr, s->priority_add, + s->irq_base, s->read_reg_select, s->elcr, + s->special_fully_nested_mode); + } +} + +void irq_info(void) +{ +#ifndef DEBUG_IRQ_COUNT + term_printf("irq statistic code not compiled.\n"); +#else + int i; + int64_t count; + + term_printf("IRQ statistics:\n"); + for (i = 0; i < 16; i++) { + count = irq_count[i]; + if (count > 0) + term_printf("%2d: %lld\n", i, count); + } +#endif +} + +PicState2 *pic_init(IRQRequestFunc *irq_request, void *irq_request_opaque) +{ + PicState2 *s; + s = qemu_mallocz(sizeof(PicState2)); + if (!s) + return NULL; + pic_init1(0x20, 0x4d0, &s->pics[0]); + pic_init1(0xa0, 0x4d1, &s->pics[1]); + s->pics[0].elcr_mask = 0xf8; + s->pics[1].elcr_mask = 0xde; + s->irq_request = irq_request; + s->irq_request_opaque = irq_request_opaque; + s->pics[0].pics_state = s; + s->pics[1].pics_state = s; + return s; +} + +void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func, + void *alt_irq_opaque) +{ + s->alt_irq_func = alt_irq_func; + s->alt_irq_opaque = alt_irq_opaque; +} diff --git a/tools/ioemu/hw/ide.c b/tools/ioemu/hw/ide.c new file mode 100644 index 0000000000..9769dfee5f --- /dev/null +++ b/tools/ioemu/hw/ide.c @@ -0,0 +1,2726 @@ +/* + * QEMU IDE disk and CD-ROM Emulator + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include + +/* debug IDE devices */ +//#define DEBUG_IDE +//#define DEBUG_IDE_ATAPI + +/* Bits of HD_STATUS */ +#define ERR_STAT 0x01 +#define INDEX_STAT 0x02 +#define ECC_STAT 0x04 /* Corrected error */ +#define DRQ_STAT 0x08 +#define SEEK_STAT 0x10 +#define SRV_STAT 0x10 +#define WRERR_STAT 0x20 +#define READY_STAT 0x40 +#define BUSY_STAT 0x80 + +/* Bits for HD_ERROR */ +#define MARK_ERR 0x01 /* Bad address mark */ +#define TRK0_ERR 0x02 /* couldn't find track 0 */ +#define ABRT_ERR 0x04 /* Command aborted */ +#define MCR_ERR 0x08 /* media change request */ +#define ID_ERR 0x10 /* ID field not found */ +#define MC_ERR 0x20 /* media changed */ +#define ECC_ERR 0x40 /* Uncorrectable ECC error */ +#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */ +#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */ + +/* Bits of HD_NSECTOR */ +#define CD 0x01 +#define IO 0x02 +#define REL 0x04 +#define TAG_MASK 0xf8 + +#define IDE_CMD_RESET 0x04 +#define IDE_CMD_DISABLE_IRQ 0x02 + +/* ATA/ATAPI Commands pre T13 Spec */ +#define WIN_NOP 0x00 +/* + * 0x01->0x02 Reserved + */ +#define CFA_REQ_EXT_ERROR_CODE 0x03 /* CFA Request Extended Error Code */ +/* + * 0x04->0x07 Reserved + */ +#define WIN_SRST 0x08 /* ATAPI soft reset command */ +#define WIN_DEVICE_RESET 0x08 +/* + * 0x09->0x0F Reserved + */ +#define WIN_RECAL 0x10 +#define WIN_RESTORE WIN_RECAL +/* + * 0x10->0x1F Reserved + */ +#define WIN_READ 0x20 /* 28-Bit */ +#define WIN_READ_ONCE 0x21 /* 28-Bit without retries */ +#define WIN_READ_LONG 0x22 /* 28-Bit */ +#define WIN_READ_LONG_ONCE 0x23 /* 28-Bit without retries */ +#define WIN_READ_EXT 0x24 /* 48-Bit */ +#define WIN_READDMA_EXT 0x25 /* 48-Bit */ +#define WIN_READDMA_QUEUED_EXT 0x26 /* 48-Bit */ +#define WIN_READ_NATIVE_MAX_EXT 0x27 /* 48-Bit */ +/* + * 0x28 + */ +#define WIN_MULTREAD_EXT 0x29 /* 48-Bit */ +/* + * 0x2A->0x2F Reserved + */ +#define WIN_WRITE 0x30 /* 28-Bit */ +#define WIN_WRITE_ONCE 0x31 /* 28-Bit without retries */ +#define WIN_WRITE_LONG 0x32 /* 28-Bit */ +#define WIN_WRITE_LONG_ONCE 0x33 /* 28-Bit without retries */ +#define WIN_WRITE_EXT 0x34 /* 48-Bit */ +#define WIN_WRITEDMA_EXT 0x35 /* 48-Bit */ +#define WIN_WRITEDMA_QUEUED_EXT 0x36 /* 48-Bit */ +#define WIN_SET_MAX_EXT 0x37 /* 48-Bit */ +#define CFA_WRITE_SECT_WO_ERASE 0x38 /* CFA Write Sectors without erase */ +#define WIN_MULTWRITE_EXT 0x39 /* 48-Bit */ +/* + * 0x3A->0x3B Reserved + */ +#define WIN_WRITE_VERIFY 0x3C /* 28-Bit */ +/* + * 0x3D->0x3F Reserved + */ +#define WIN_VERIFY 0x40 /* 28-Bit - Read Verify Sectors */ +#define WIN_VERIFY_ONCE 0x41 /* 28-Bit - without retries */ +#define WIN_VERIFY_EXT 0x42 /* 48-Bit */ +/* + * 0x43->0x4F Reserved + */ +#define WIN_FORMAT 0x50 +/* + * 0x51->0x5F Reserved + */ +#define WIN_INIT 0x60 +/* + * 0x61->0x5F Reserved + */ +#define WIN_SEEK 0x70 /* 0x70-0x7F Reserved */ +#define CFA_TRANSLATE_SECTOR 0x87 /* CFA Translate Sector */ +#define WIN_DIAGNOSE 0x90 +#define WIN_SPECIFY 0x91 /* set drive geometry translation */ +#define WIN_DOWNLOAD_MICROCODE 0x92 +#define WIN_STANDBYNOW2 0x94 +#define WIN_STANDBY2 0x96 +#define WIN_SETIDLE2 0x97 +#define WIN_CHECKPOWERMODE2 0x98 +#define WIN_SLEEPNOW2 0x99 +/* + * 0x9A VENDOR + */ +#define WIN_PACKETCMD 0xA0 /* Send a packet command. */ +#define WIN_PIDENTIFY 0xA1 /* identify ATAPI device */ +#define WIN_QUEUED_SERVICE 0xA2 +#define WIN_SMART 0xB0 /* self-monitoring and reporting */ +#define CFA_ERASE_SECTORS 0xC0 +#define WIN_MULTREAD 0xC4 /* read sectors using multiple mode*/ +#define WIN_MULTWRITE 0xC5 /* write sectors using multiple mode */ +#define WIN_SETMULT 0xC6 /* enable/disable multiple mode */ +#define WIN_READDMA_QUEUED 0xC7 /* read sectors using Queued DMA transfers */ +#define WIN_READDMA 0xC8 /* read sectors using DMA transfers */ +#define WIN_READDMA_ONCE 0xC9 /* 28-Bit - without retries */ +#define WIN_WRITEDMA 0xCA /* write sectors using DMA transfers */ +#define WIN_WRITEDMA_ONCE 0xCB /* 28-Bit - without retries */ +#define WIN_WRITEDMA_QUEUED 0xCC /* write sectors using Queued DMA transfers */ +#define CFA_WRITE_MULTI_WO_ERASE 0xCD /* CFA Write multiple without erase */ +#define WIN_GETMEDIASTATUS 0xDA +#define WIN_ACKMEDIACHANGE 0xDB /* ATA-1, ATA-2 vendor */ +#define WIN_POSTBOOT 0xDC +#define WIN_PREBOOT 0xDD +#define WIN_DOORLOCK 0xDE /* lock door on removable drives */ +#define WIN_DOORUNLOCK 0xDF /* unlock door on removable drives */ +#define WIN_STANDBYNOW1 0xE0 +#define WIN_IDLEIMMEDIATE 0xE1 /* force drive to become "ready" */ +#define WIN_STANDBY 0xE2 /* Set device in Standby Mode */ +#define WIN_SETIDLE1 0xE3 +#define WIN_READ_BUFFER 0xE4 /* force read only 1 sector */ +#define WIN_CHECKPOWERMODE1 0xE5 +#define WIN_SLEEPNOW1 0xE6 +#define WIN_FLUSH_CACHE 0xE7 +#define WIN_WRITE_BUFFER 0xE8 /* force write only 1 sector */ +#define WIN_WRITE_SAME 0xE9 /* read ata-2 to use */ + /* SET_FEATURES 0x22 or 0xDD */ +#define WIN_FLUSH_CACHE_EXT 0xEA /* 48-Bit */ +#define WIN_IDENTIFY 0xEC /* ask drive to identify itself */ +#define WIN_MEDIAEJECT 0xED +#define WIN_IDENTIFY_DMA 0xEE /* same as WIN_IDENTIFY, but DMA */ +#define WIN_SETFEATURES 0xEF /* set special drive features */ +#define EXABYTE_ENABLE_NEST 0xF0 +#define WIN_SECURITY_SET_PASS 0xF1 +#define WIN_SECURITY_UNLOCK 0xF2 +#define WIN_SECURITY_ERASE_PREPARE 0xF3 +#define WIN_SECURITY_ERASE_UNIT 0xF4 +#define WIN_SECURITY_FREEZE_LOCK 0xF5 +#define WIN_SECURITY_DISABLE 0xF6 +#define WIN_READ_NATIVE_MAX 0xF8 /* return the native maximum address */ +#define WIN_SET_MAX 0xF9 +#define DISABLE_SEAGATE 0xFB + +/* set to 1 set disable mult support */ +#define MAX_MULT_SECTORS 16 + +/* ATAPI defines */ + +#define ATAPI_PACKET_SIZE 12 + +/* The generic packet command opcodes for CD/DVD Logical Units, + * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */ +#define GPCMD_BLANK 0xa1 +#define GPCMD_CLOSE_TRACK 0x5b +#define GPCMD_FLUSH_CACHE 0x35 +#define GPCMD_FORMAT_UNIT 0x04 +#define GPCMD_GET_CONFIGURATION 0x46 +#define GPCMD_GET_EVENT_STATUS_NOTIFICATION 0x4a +#define GPCMD_GET_PERFORMANCE 0xac +#define GPCMD_INQUIRY 0x12 +#define GPCMD_LOAD_UNLOAD 0xa6 +#define GPCMD_MECHANISM_STATUS 0xbd +#define GPCMD_MODE_SELECT_10 0x55 +#define GPCMD_MODE_SENSE_10 0x5a +#define GPCMD_PAUSE_RESUME 0x4b +#define GPCMD_PLAY_AUDIO_10 0x45 +#define GPCMD_PLAY_AUDIO_MSF 0x47 +#define GPCMD_PLAY_AUDIO_TI 0x48 +#define GPCMD_PLAY_CD 0xbc +#define GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#define GPCMD_READ_10 0x28 +#define GPCMD_READ_12 0xa8 +#define GPCMD_READ_CDVD_CAPACITY 0x25 +#define GPCMD_READ_CD 0xbe +#define GPCMD_READ_CD_MSF 0xb9 +#define GPCMD_READ_DISC_INFO 0x51 +#define GPCMD_READ_DVD_STRUCTURE 0xad +#define GPCMD_READ_FORMAT_CAPACITIES 0x23 +#define GPCMD_READ_HEADER 0x44 +#define GPCMD_READ_TRACK_RZONE_INFO 0x52 +#define GPCMD_READ_SUBCHANNEL 0x42 +#define GPCMD_READ_TOC_PMA_ATIP 0x43 +#define GPCMD_REPAIR_RZONE_TRACK 0x58 +#define GPCMD_REPORT_KEY 0xa4 +#define GPCMD_REQUEST_SENSE 0x03 +#define GPCMD_RESERVE_RZONE_TRACK 0x53 +#define GPCMD_SCAN 0xba +#define GPCMD_SEEK 0x2b +#define GPCMD_SEND_DVD_STRUCTURE 0xad +#define GPCMD_SEND_EVENT 0xa2 +#define GPCMD_SEND_KEY 0xa3 +#define GPCMD_SEND_OPC 0x54 +#define GPCMD_SET_READ_AHEAD 0xa7 +#define GPCMD_SET_STREAMING 0xb6 +#define GPCMD_START_STOP_UNIT 0x1b +#define GPCMD_STOP_PLAY_SCAN 0x4e +#define GPCMD_TEST_UNIT_READY 0x00 +#define GPCMD_VERIFY_10 0x2f +#define GPCMD_WRITE_10 0x2a +#define GPCMD_WRITE_AND_VERIFY_10 0x2e +/* This is listed as optional in ATAPI 2.6, but is (curiously) + * missing from Mt. Fuji, Table 57. It _is_ mentioned in Mt. Fuji + * Table 377 as an MMC command for SCSi devices though... Most ATAPI + * drives support it. */ +#define GPCMD_SET_SPEED 0xbb +/* This seems to be a SCSI specific CD-ROM opcode + * to play data at track/index */ +#define GPCMD_PLAYAUDIO_TI 0x48 +/* + * From MS Media Status Notification Support Specification. For + * older drives only. + */ +#define GPCMD_GET_MEDIA_STATUS 0xda + +/* Mode page codes for mode sense/set */ +#define GPMODE_R_W_ERROR_PAGE 0x01 +#define GPMODE_WRITE_PARMS_PAGE 0x05 +#define GPMODE_AUDIO_CTL_PAGE 0x0e +#define GPMODE_POWER_PAGE 0x1a +#define GPMODE_FAULT_FAIL_PAGE 0x1c +#define GPMODE_TO_PROTECT_PAGE 0x1d +#define GPMODE_CAPABILITIES_PAGE 0x2a +#define GPMODE_ALL_PAGES 0x3f +/* Not in Mt. Fuji, but in ATAPI 2.6 -- depricated now in favor + * of MODE_SENSE_POWER_PAGE */ +#define GPMODE_CDROM_PAGE 0x0d + +#define ATAPI_INT_REASON_CD 0x01 /* 0 = data transfer */ +#define ATAPI_INT_REASON_IO 0x02 /* 1 = transfer to the host */ +#define ATAPI_INT_REASON_REL 0x04 +#define ATAPI_INT_REASON_TAG 0xf8 + +/* same constants as bochs */ +#define ASC_ILLEGAL_OPCODE 0x20 +#define ASC_LOGICAL_BLOCK_OOR 0x21 +#define ASC_INV_FIELD_IN_CMD_PACKET 0x24 +#define ASC_MEDIUM_NOT_PRESENT 0x3a +#define ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39 + +#define SENSE_NONE 0 +#define SENSE_NOT_READY 2 +#define SENSE_ILLEGAL_REQUEST 5 +#define SENSE_UNIT_ATTENTION 6 + +struct IDEState; + +typedef void EndTransferFunc(struct IDEState *); + +/* NOTE: IDEState represents in fact one drive */ +typedef struct IDEState { + /* ide config */ + int is_cdrom; + int cylinders, heads, sectors; + int64_t nb_sectors; + int mult_sectors; + int identify_set; + uint16_t identify_data[256]; + SetIRQFunc *set_irq; + void *irq_opaque; + int irq; + PCIDevice *pci_dev; + struct BMDMAState *bmdma; + int drive_serial; + /* ide regs */ + uint8_t feature; + uint8_t error; + uint32_t nsector; + uint8_t sector; + uint8_t lcyl; + uint8_t hcyl; + /* other part of tf for lba48 support */ + uint8_t hob_feature; + uint8_t hob_nsector; + uint8_t hob_sector; + uint8_t hob_lcyl; + uint8_t hob_hcyl; + + uint8_t select; + uint8_t status; + + /* 0x3f6 command, only meaningful for drive 0 */ + uint8_t cmd; + /* set for lba48 access */ + uint8_t lba48; + /* depends on bit 4 in select, only meaningful for drive 0 */ + struct IDEState *cur_drive; + BlockDriverState *bs; + /* ATAPI specific */ + uint8_t sense_key; + uint8_t asc; + int packet_transfer_size; + int elementary_transfer_size; + int io_buffer_index; + int lba; + int cd_sector_size; + int atapi_dma; /* true if dma is requested for the packet cmd */ + /* ATA DMA state */ + int io_buffer_size; + /* PIO transfer handling */ + int req_nb_sectors; /* number of sectors per interrupt */ + EndTransferFunc *end_transfer_func; + uint8_t *data_ptr; + uint8_t *data_end; + uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4]; + QEMUTimer *sector_write_timer; /* only used for win2k instal hack */ + uint32_t irq_count; /* counts IRQs when using win2k install hack */ +} IDEState; + +#define BM_STATUS_DMAING 0x01 +#define BM_STATUS_ERROR 0x02 +#define BM_STATUS_INT 0x04 + +#define BM_CMD_START 0x01 +#define BM_CMD_READ 0x08 + +#define IDE_TYPE_PIIX3 0 +#define IDE_TYPE_CMD646 1 + +/* CMD646 specific */ +#define MRDMODE 0x71 +#define MRDMODE_INTR_CH0 0x04 +#define MRDMODE_INTR_CH1 0x08 +#define MRDMODE_BLK_CH0 0x10 +#define MRDMODE_BLK_CH1 0x20 +#define UDIDETCR0 0x73 +#define UDIDETCR1 0x7B + +typedef int IDEDMAFunc(IDEState *s, + target_phys_addr_t phys_addr, + int transfer_size1); + +typedef struct BMDMAState { + uint8_t cmd; + uint8_t status; + uint32_t addr; + + struct PCIIDEState *pci_dev; + /* current transfer state */ + IDEState *ide_if; + IDEDMAFunc *dma_cb; +} BMDMAState; + +typedef struct PCIIDEState { + PCIDevice dev; + IDEState ide_if[4]; + BMDMAState bmdma[2]; + int type; /* see IDE_TYPE_xxx */ +} PCIIDEState; + +#define DMA_MULTI_THREAD + +#ifdef DMA_MULTI_THREAD + +static int file_pipes[2]; + +static void ide_dma_loop(BMDMAState *bm); +static void dma_thread_loop(BMDMAState *bm); + +static void *dma_thread_func(void* opaque) +{ + BMDMAState* req; + + while (read(file_pipes[0], &req, sizeof(req))) { + dma_thread_loop(req); + } + + return NULL; +} + +static void dma_create_thread(void) +{ + pthread_t tid; + int rt; + + if (pipe(file_pipes) != 0) { + fprintf(stderr, "create pipe failed\n"); + exit(1); + } + + if ((rt = pthread_create(&tid, NULL, dma_thread_func, NULL))) { + fprintf(stderr, "Oops, dma thread creation failed, errno=%d\n", rt); + exit(1); + } + + if ((rt = pthread_detach(tid))) { + fprintf(stderr, "Oops, dma thread detachment failed, errno=%d\n", rt); + exit(1); + } +} +#endif /* DMA_MULTI_THREAD */ + +static void ide_dma_start(IDEState *s, IDEDMAFunc *dma_cb); + +static void padstr(char *str, const char *src, int len) +{ + int i, v; + for(i = 0; i < len; i++) { + if (*src) + v = *src++; + else + v = ' '; + *(char *)((long)str ^ 1) = v; + str++; + } +} + +static void padstr8(uint8_t *buf, int buf_size, const char *src) +{ + int i; + for(i = 0; i < buf_size; i++) { + if (*src) + buf[i] = *src++; + else + buf[i] = ' '; + } +} + +static void put_le16(uint16_t *p, unsigned int v) +{ + *p = cpu_to_le16(v); +} + +static void ide_identify(IDEState *s) +{ + uint16_t *p; + unsigned int oldsize; + char buf[20]; + + if (s->identify_set) { + memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data)); + return; + } + + memset(s->io_buffer, 0, 512); + p = (uint16_t *)s->io_buffer; + put_le16(p + 0, 0x0040); + put_le16(p + 1, s->cylinders); + put_le16(p + 3, s->heads); + put_le16(p + 4, 512 * s->sectors); /* XXX: retired, remove ? */ + put_le16(p + 5, 512); /* XXX: retired, remove ? */ + put_le16(p + 6, s->sectors); + snprintf(buf, sizeof(buf), "QM%05d", s->drive_serial); + padstr((uint8_t *)(p + 10), buf, 20); /* serial number */ + put_le16(p + 20, 3); /* XXX: retired, remove ? */ + put_le16(p + 21, 512); /* cache size in sectors */ + put_le16(p + 22, 4); /* ecc bytes */ + padstr((uint8_t *)(p + 23), QEMU_VERSION, 8); /* firmware version */ + padstr((uint8_t *)(p + 27), "QEMU HARDDISK", 40); /* model */ +#if MAX_MULT_SECTORS > 1 + put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS); +#endif + put_le16(p + 48, 1); /* dword I/O */ + put_le16(p + 49, (1 << 11) | (1 << 9) | (1 << 8)); /* DMA and LBA supported */ + put_le16(p + 51, 0x200); /* PIO transfer cycle */ + put_le16(p + 52, 0x200); /* DMA transfer cycle */ + put_le16(p + 53, 1 | (1 << 1) | (1 << 2)); /* words 54-58,64-70,88 are valid */ + put_le16(p + 54, s->cylinders); + put_le16(p + 55, s->heads); + put_le16(p + 56, s->sectors); + oldsize = s->cylinders * s->heads * s->sectors; + put_le16(p + 57, oldsize); + put_le16(p + 58, oldsize >> 16); + if (s->mult_sectors) + put_le16(p + 59, 0x100 | s->mult_sectors); + put_le16(p + 60, s->nb_sectors); + put_le16(p + 61, s->nb_sectors >> 16); + put_le16(p + 63, 0x07); /* mdma0-2 supported */ + put_le16(p + 65, 120); + put_le16(p + 66, 120); + put_le16(p + 67, 120); + put_le16(p + 68, 120); + put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */ + put_le16(p + 81, 0x16); /* conforms to ata5 */ + put_le16(p + 82, (1 << 14)); + /* 13=flush_cache_ext,12=flush_cache,10=lba48 */ + put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10)); + put_le16(p + 84, (1 << 14)); + put_le16(p + 85, (1 << 14)); + /* 13=flush_cache_ext,12=flush_cache,10=lba48 */ + put_le16(p + 86, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10)); + put_le16(p + 87, (1 << 14)); + put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */ + put_le16(p + 93, 1 | (1 << 14) | 0x2000); + put_le16(p + 100, s->nb_sectors); + put_le16(p + 101, s->nb_sectors >> 16); + put_le16(p + 102, s->nb_sectors >> 32); + put_le16(p + 103, s->nb_sectors >> 48); + + memcpy(s->identify_data, p, sizeof(s->identify_data)); + s->identify_set = 1; +} + +static void ide_atapi_identify(IDEState *s) +{ + uint16_t *p; + char buf[20]; + + if (s->identify_set) { + memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data)); + return; + } + + memset(s->io_buffer, 0, 512); + p = (uint16_t *)s->io_buffer; + /* Removable CDROM, 50us response, 12 byte packets */ + put_le16(p + 0, (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0)); + snprintf(buf, sizeof(buf), "QM%05d", s->drive_serial); + padstr((uint8_t *)(p + 10), buf, 20); /* serial number */ + put_le16(p + 20, 3); /* buffer type */ + put_le16(p + 21, 512); /* cache size in sectors */ + put_le16(p + 22, 4); /* ecc bytes */ + padstr((uint8_t *)(p + 23), QEMU_VERSION, 8); /* firmware version */ + padstr((uint8_t *)(p + 27), "QEMU CD-ROM", 40); /* model */ + put_le16(p + 48, 1); /* dword I/O (XXX: should not be set on CDROM) */ + put_le16(p + 49, 1 << 9); /* LBA supported, no DMA */ + put_le16(p + 53, 3); /* words 64-70, 54-58 valid */ + put_le16(p + 63, 0x103); /* DMA modes XXX: may be incorrect */ + put_le16(p + 64, 1); /* PIO modes */ + put_le16(p + 65, 0xb4); /* minimum DMA multiword tx cycle time */ + put_le16(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */ + put_le16(p + 67, 0x12c); /* minimum PIO cycle time without flow control */ + put_le16(p + 68, 0xb4); /* minimum PIO cycle time with IORDY flow control */ + + put_le16(p + 71, 30); /* in ns */ + put_le16(p + 72, 30); /* in ns */ + + put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */ + + memcpy(s->identify_data, p, sizeof(s->identify_data)); + s->identify_set = 1; +} + +static void ide_set_signature(IDEState *s) +{ + s->select &= 0xf0; /* clear head */ + /* put signature */ + s->nsector = 1; + s->sector = 1; + if (s->is_cdrom) { + s->lcyl = 0x14; + s->hcyl = 0xeb; + } else if (s->bs) { + s->lcyl = 0; + s->hcyl = 0; + } else { + s->lcyl = 0xff; + s->hcyl = 0xff; + } +} + +static inline void ide_abort_command(IDEState *s) +{ + s->status = READY_STAT | ERR_STAT; + s->error = ABRT_ERR; +} + +static inline void ide_set_irq(IDEState *s) +{ + BMDMAState *bm = s->bmdma; + if (!(s->cmd & IDE_CMD_DISABLE_IRQ)) { + if (bm) { + bm->status |= BM_STATUS_INT; + } + s->set_irq(s->irq_opaque, s->irq, 1); + } +} + +/* prepare data transfer and tell what to do after */ +static void ide_transfer_start(IDEState *s, uint8_t *buf, int size, + EndTransferFunc *end_transfer_func) +{ + s->end_transfer_func = end_transfer_func; + s->data_ptr = buf; + s->data_end = buf + size; + s->status |= DRQ_STAT; +} + +static void ide_transfer_stop(IDEState *s) +{ + s->end_transfer_func = ide_transfer_stop; + s->data_ptr = s->io_buffer; + s->data_end = s->io_buffer; + s->status &= ~DRQ_STAT; +} + +static int64_t ide_get_sector(IDEState *s) +{ + int64_t sector_num; + if (s->select & 0x40) { + /* lba */ + if (!s->lba48) { + sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) | + (s->lcyl << 8) | s->sector; + } else { + sector_num = ((int64_t)s->hob_hcyl << 40) | + ((int64_t) s->hob_lcyl << 32) | + ((int64_t) s->hob_sector << 24) | + ((int64_t) s->hcyl << 16) | + ((int64_t) s->lcyl << 8) | s->sector; + } + } else { + sector_num = ((s->hcyl << 8) | s->lcyl) * s->heads * s->sectors + + (s->select & 0x0f) * s->sectors + (s->sector - 1); + } + return sector_num; +} + +static void ide_set_sector(IDEState *s, int64_t sector_num) +{ + unsigned int cyl, r; + if (s->select & 0x40) { + if (!s->lba48) { + s->select = (s->select & 0xf0) | (sector_num >> 24); + s->hcyl = (sector_num >> 16); + s->lcyl = (sector_num >> 8); + s->sector = (sector_num); + } else { + s->sector = sector_num; + s->lcyl = sector_num >> 8; + s->hcyl = sector_num >> 16; + s->hob_sector = sector_num >> 24; + s->hob_lcyl = sector_num >> 32; + s->hob_hcyl = sector_num >> 40; + } + } else { + cyl = sector_num / (s->heads * s->sectors); + r = sector_num % (s->heads * s->sectors); + s->hcyl = cyl >> 8; + s->lcyl = cyl; + s->select = (s->select & 0xf0) | ((r / s->sectors) & 0x0f); + s->sector = (r % s->sectors) + 1; + } +} + +static void ide_sector_read(IDEState *s) +{ + int64_t sector_num; + int ret, n; + + s->status = READY_STAT | SEEK_STAT; + s->error = 0; /* not needed by IDE spec, but needed by Windows */ + sector_num = ide_get_sector(s); + n = s->nsector; + if (n == 0) { + /* no more sector to read from disk */ + ide_transfer_stop(s); + } else { +#if defined(DEBUG_IDE) + printf("read sector=%Ld\n", sector_num); +#endif + if (n > s->req_nb_sectors) + n = s->req_nb_sectors; + ret = bdrv_read(s->bs, sector_num, s->io_buffer, n); + ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_read); + ide_set_irq(s); + ide_set_sector(s, sector_num + n); + s->nsector -= n; + } +} + +static int ide_read_dma_cb(IDEState *s, + target_phys_addr_t phys_addr, + int transfer_size1) +{ + int len, transfer_size, n; + int64_t sector_num; + + transfer_size = transfer_size1; + while (transfer_size > 0) { + len = s->io_buffer_size - s->io_buffer_index; + if (len <= 0) { + /* transfert next data */ + n = s->nsector; + if (n == 0) + break; + if (n > MAX_MULT_SECTORS) + n = MAX_MULT_SECTORS; + sector_num = ide_get_sector(s); + bdrv_read(s->bs, sector_num, s->io_buffer, n); + s->io_buffer_index = 0; + s->io_buffer_size = n * 512; + len = s->io_buffer_size; + sector_num += n; + ide_set_sector(s, sector_num); + s->nsector -= n; + } + if (len > transfer_size) + len = transfer_size; + cpu_physical_memory_write(phys_addr, + s->io_buffer + s->io_buffer_index, len); + s->io_buffer_index += len; + transfer_size -= len; + phys_addr += len; + } + if (s->io_buffer_index >= s->io_buffer_size && s->nsector == 0) { + s->status = READY_STAT | SEEK_STAT; +#ifndef DMA_MULTI_THREAD + ide_set_irq(s); +#endif /* !DMA_MULTI_THREAD */ +#ifdef DEBUG_IDE_ATAPI + printf("dma status=0x%x\n", s->status); +#endif + return 0; + } + return transfer_size1 - transfer_size; +} + +static void ide_sector_read_dma(IDEState *s) +{ + s->status = READY_STAT | SEEK_STAT | DRQ_STAT; + s->io_buffer_index = 0; + s->io_buffer_size = 0; + ide_dma_start(s, ide_read_dma_cb); +} + +static void ide_sector_write_timer_cb(void *opaque) +{ + IDEState *s = opaque; + ide_set_irq(s); +} + +static void ide_sector_write(IDEState *s) +{ + int64_t sector_num; + int ret, n, n1; + + s->status = READY_STAT | SEEK_STAT; + sector_num = ide_get_sector(s); +#if defined(DEBUG_IDE) + printf("write sector=%Ld\n", sector_num); +#endif + n = s->nsector; + if (n > s->req_nb_sectors) + n = s->req_nb_sectors; + ret = bdrv_write(s->bs, sector_num, s->io_buffer, n); + s->nsector -= n; + if (s->nsector == 0) { + /* no more sector to write */ + ide_transfer_stop(s); + } else { + n1 = s->nsector; + if (n1 > s->req_nb_sectors) + n1 = s->req_nb_sectors; + ide_transfer_start(s, s->io_buffer, 512 * n1, ide_sector_write); + } + ide_set_sector(s, sector_num + n); + +#ifdef TARGET_I386 + if (win2k_install_hack && ((++s->irq_count % 16) == 0)) { + /* It seems there is a bug in the Windows 2000 installer HDD + IDE driver which fills the disk with empty logs when the + IDE write IRQ comes too early. This hack tries to correct + that at the expense of slower write performances. Use this + option _only_ to install Windows 2000. You must disable it + for normal use. */ + qemu_mod_timer(s->sector_write_timer, + qemu_get_clock(vm_clock) + (ticks_per_sec / 1000)); + } else +#endif + { + ide_set_irq(s); + } +} + +static int ide_write_dma_cb(IDEState *s, + target_phys_addr_t phys_addr, + int transfer_size1) +{ + int len, transfer_size, n; + int64_t sector_num; + + transfer_size = transfer_size1; + for(;;) { + len = s->io_buffer_size - s->io_buffer_index; + if (len == 0) { + n = s->io_buffer_size >> 9; + sector_num = ide_get_sector(s); + bdrv_write(s->bs, sector_num, s->io_buffer, + s->io_buffer_size >> 9); + sector_num += n; + ide_set_sector(s, sector_num); + s->nsector -= n; + n = s->nsector; + if (n == 0) { + /* end of transfer */ + s->status = READY_STAT | SEEK_STAT; +#ifdef TARGET_I386 + if (win2k_install_hack && ((++s->irq_count % 16) == 0)) { + /* It seems there is a bug in the Windows 2000 installer + HDD IDE driver which fills the disk with empty logs + when the IDE write IRQ comes too early. This hack tries + to correct that at the expense of slower write + performances. Use this option _only_ to install Windows + 2000. You must disable it for normal use. */ + qemu_mod_timer(s->sector_write_timer, + qemu_get_clock(vm_clock) + (ticks_per_sec / 1000)); + } else +#endif +#ifndef DMA_MULTI_THREAD + ide_set_irq(s); +#else /* !DMA_MULTI_THREAD */ + ; +#endif /* DMA_MULTI_THREAD */ + return 0; + } + if (n > MAX_MULT_SECTORS) + n = MAX_MULT_SECTORS; + s->io_buffer_index = 0; + s->io_buffer_size = n * 512; + len = s->io_buffer_size; + } + if (transfer_size <= 0) + break; + if (len > transfer_size) + len = transfer_size; + cpu_physical_memory_read(phys_addr, + s->io_buffer + s->io_buffer_index, len); + s->io_buffer_index += len; + transfer_size -= len; + phys_addr += len; + } + return transfer_size1 - transfer_size; +} + +static void ide_sector_write_dma(IDEState *s) +{ + int n; + s->status = READY_STAT | SEEK_STAT | DRQ_STAT; + n = s->nsector; + if (n > MAX_MULT_SECTORS) + n = MAX_MULT_SECTORS; + s->io_buffer_index = 0; + s->io_buffer_size = n * 512; + ide_dma_start(s, ide_write_dma_cb); +} + +static void ide_atapi_cmd_ok(IDEState *s) +{ + s->error = 0; + s->status = READY_STAT; + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; + ide_set_irq(s); +} + +static void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc) +{ +#ifdef DEBUG_IDE_ATAPI + printf("atapi_cmd_error: sense=0x%x asc=0x%x\n", sense_key, asc); +#endif + s->error = sense_key << 4; + s->status = READY_STAT | ERR_STAT; + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; + s->sense_key = sense_key; + s->asc = asc; + ide_set_irq(s); +} + +static inline void cpu_to_ube16(uint8_t *buf, int val) +{ + buf[0] = val >> 8; + buf[1] = val; +} + +static inline void cpu_to_ube32(uint8_t *buf, unsigned int val) +{ + buf[0] = val >> 24; + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val; +} + +static inline int ube16_to_cpu(const uint8_t *buf) +{ + return (buf[0] << 8) | buf[1]; +} + +static inline int ube32_to_cpu(const uint8_t *buf) +{ + return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; +} + +static void lba_to_msf(uint8_t *buf, int lba) +{ + lba += 150; + buf[0] = (lba / 75) / 60; + buf[1] = (lba / 75) % 60; + buf[2] = lba % 75; +} + +static void cd_read_sector(BlockDriverState *bs, int lba, uint8_t *buf, + int sector_size) +{ + switch(sector_size) { + case 2048: + bdrv_read(bs, (int64_t)lba << 2, buf, 4); + break; + case 2352: + /* sync bytes */ + buf[0] = 0x00; + memset(buf + 1, 0xff, 10); + buf[11] = 0x00; + buf += 12; + /* MSF */ + lba_to_msf(buf, lba); + buf[3] = 0x01; /* mode 1 data */ + buf += 4; + /* data */ + bdrv_read(bs, (int64_t)lba << 2, buf, 4); + buf += 2048; + /* ECC */ + memset(buf, 0, 288); + break; + default: + break; + } +} + +/* The whole ATAPI transfer logic is handled in this function */ +static void ide_atapi_cmd_reply_end(IDEState *s) +{ + int byte_count_limit, size; +#ifdef DEBUG_IDE_ATAPI + printf("reply: tx_size=%d elem_tx_size=%d index=%d\n", + s->packet_transfer_size, + s->elementary_transfer_size, + s->io_buffer_index); +#endif + if (s->packet_transfer_size <= 0) { + /* end of transfer */ + ide_transfer_stop(s); + s->status = READY_STAT; + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; + ide_set_irq(s); +#ifdef DEBUG_IDE_ATAPI + printf("status=0x%x\n", s->status); +#endif + } else { + /* see if a new sector must be read */ + if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) { + cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size); + s->lba++; + s->io_buffer_index = 0; + } + if (s->elementary_transfer_size > 0) { + /* there are some data left to transmit in this elementary + transfer */ + size = s->cd_sector_size - s->io_buffer_index; + if (size > s->elementary_transfer_size) + size = s->elementary_transfer_size; + ide_transfer_start(s, s->io_buffer + s->io_buffer_index, + size, ide_atapi_cmd_reply_end); + s->packet_transfer_size -= size; + s->elementary_transfer_size -= size; + s->io_buffer_index += size; + } else { + /* a new transfer is needed */ + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; + byte_count_limit = s->lcyl | (s->hcyl << 8); +#ifdef DEBUG_IDE_ATAPI + printf("byte_count_limit=%d\n", byte_count_limit); +#endif + if (byte_count_limit == 0xffff) + byte_count_limit--; + size = s->packet_transfer_size; + if (size > byte_count_limit) { + /* byte count limit must be even if this case */ + if (byte_count_limit & 1) + byte_count_limit--; + size = byte_count_limit; + } + s->lcyl = size; + s->hcyl = size >> 8; + s->elementary_transfer_size = size; + /* we cannot transmit more than one sector at a time */ + if (s->lba != -1) { + if (size > (s->cd_sector_size - s->io_buffer_index)) + size = (s->cd_sector_size - s->io_buffer_index); + } + ide_transfer_start(s, s->io_buffer + s->io_buffer_index, + size, ide_atapi_cmd_reply_end); + s->packet_transfer_size -= size; + s->elementary_transfer_size -= size; + s->io_buffer_index += size; + ide_set_irq(s); +#ifdef DEBUG_IDE_ATAPI + printf("status=0x%x\n", s->status); +#endif + } + } +} + +/* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */ +static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size) +{ + if (size > max_size) + size = max_size; + s->lba = -1; /* no sector read */ + s->packet_transfer_size = size; + s->elementary_transfer_size = 0; + s->io_buffer_index = 0; + + s->status = READY_STAT; + ide_atapi_cmd_reply_end(s); +} + +/* start a CD-CDROM read command */ +static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors, + int sector_size) +{ + s->lba = lba; + s->packet_transfer_size = nb_sectors * sector_size; + s->elementary_transfer_size = 0; + s->io_buffer_index = sector_size; + s->cd_sector_size = sector_size; + + s->status = READY_STAT; + ide_atapi_cmd_reply_end(s); +} + +/* ATAPI DMA support */ +static int ide_atapi_cmd_read_dma_cb(IDEState *s, + target_phys_addr_t phys_addr, + int transfer_size1) +{ + int len, transfer_size; + + transfer_size = transfer_size1; + while (transfer_size > 0) { +#ifdef DEBUG_IDE_ATAPI + printf("transfer_size: %d phys_addr=%08x\n", transfer_size, phys_addr); +#endif + if (s->packet_transfer_size <= 0) + break; + len = s->cd_sector_size - s->io_buffer_index; + if (len <= 0) { + /* transfert next data */ + cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size); + s->lba++; + s->io_buffer_index = 0; + len = s->cd_sector_size; + } + if (len > transfer_size) + len = transfer_size; + cpu_physical_memory_write(phys_addr, + s->io_buffer + s->io_buffer_index, len); + s->packet_transfer_size -= len; + s->io_buffer_index += len; + transfer_size -= len; + phys_addr += len; + } + if (s->packet_transfer_size <= 0) { + s->status = READY_STAT; + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; +#ifndef DMA_MULTI_THREAD + ide_set_irq(s); +#endif /* !DMA_MULTI_THREAD */ +#ifdef DEBUG_IDE_ATAPI + printf("dma status=0x%x\n", s->status); +#endif + return 0; + } + return transfer_size1 - transfer_size; +} + +/* start a CD-CDROM read command with DMA */ +/* XXX: test if DMA is available */ +static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors, + int sector_size) +{ + s->lba = lba; + s->packet_transfer_size = nb_sectors * sector_size; + s->io_buffer_index = sector_size; + s->cd_sector_size = sector_size; + + s->status = READY_STAT | DRQ_STAT; + ide_dma_start(s, ide_atapi_cmd_read_dma_cb); +} + +static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors, + int sector_size) +{ +#ifdef DEBUG_IDE_ATAPI + printf("read: LBA=%d nb_sectors=%d\n", lba, nb_sectors); +#endif + if (s->atapi_dma) { + ide_atapi_cmd_read_dma(s, lba, nb_sectors, sector_size); + } else { + ide_atapi_cmd_read_pio(s, lba, nb_sectors, sector_size); + } +} + +/* same toc as bochs. Return -1 if error or the toc length */ +/* XXX: check this */ +static int cdrom_read_toc(IDEState *s, uint8_t *buf, int msf, int start_track) +{ + uint8_t *q; + int nb_sectors, len; + + if (start_track > 1 && start_track != 0xaa) + return -1; + q = buf + 2; + *q++ = 1; /* first session */ + *q++ = 1; /* last session */ + if (start_track <= 1) { + *q++ = 0; /* reserved */ + *q++ = 0x14; /* ADR, control */ + *q++ = 1; /* track number */ + *q++ = 0; /* reserved */ + if (msf) { + *q++ = 0; /* reserved */ + lba_to_msf(q, 0); + q += 3; + } else { + /* sector 0 */ + cpu_to_ube32(q, 0); + q += 4; + } + } + /* lead out track */ + *q++ = 0; /* reserved */ + *q++ = 0x16; /* ADR, control */ + *q++ = 0xaa; /* track number */ + *q++ = 0; /* reserved */ + nb_sectors = s->nb_sectors >> 2; + if (msf) { + *q++ = 0; /* reserved */ + lba_to_msf(q, nb_sectors); + q += 3; + } else { + cpu_to_ube32(q, nb_sectors); + q += 4; + } + len = q - buf; + cpu_to_ube16(buf, len - 2); + return len; +} + +/* mostly same info as PearPc */ +static int cdrom_read_toc_raw(IDEState *s, uint8_t *buf, int msf, + int session_num) +{ + uint8_t *q; + int nb_sectors, len; + + q = buf + 2; + *q++ = 1; /* first session */ + *q++ = 1; /* last session */ + + *q++ = 1; /* session number */ + *q++ = 0x14; /* data track */ + *q++ = 0; /* track number */ + *q++ = 0xa0; /* lead-in */ + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + *q++ = 0; + *q++ = 1; /* first track */ + *q++ = 0x00; /* disk type */ + *q++ = 0x00; + + *q++ = 1; /* session number */ + *q++ = 0x14; /* data track */ + *q++ = 0; /* track number */ + *q++ = 0xa1; + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + *q++ = 0; + *q++ = 1; /* last track */ + *q++ = 0x00; + *q++ = 0x00; + + *q++ = 1; /* session number */ + *q++ = 0x14; /* data track */ + *q++ = 0; /* track number */ + *q++ = 0xa2; /* lead-out */ + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + nb_sectors = s->nb_sectors >> 2; + if (msf) { + *q++ = 0; /* reserved */ + lba_to_msf(q, nb_sectors); + q += 3; + } else { + cpu_to_ube32(q, nb_sectors); + q += 4; + } + + *q++ = 1; /* session number */ + *q++ = 0x14; /* ADR, control */ + *q++ = 0; /* track number */ + *q++ = 1; /* point */ + *q++ = 0; /* min */ + *q++ = 0; /* sec */ + *q++ = 0; /* frame */ + if (msf) { + *q++ = 0; + lba_to_msf(q, 0); + q += 3; + } else { + *q++ = 0; + *q++ = 0; + *q++ = 0; + *q++ = 0; + } + + len = q - buf; + cpu_to_ube16(buf, len - 2); + return len; +} + +static void ide_atapi_cmd(IDEState *s) +{ + const uint8_t *packet; + uint8_t *buf; + int max_len; + + packet = s->io_buffer; + buf = s->io_buffer; +#ifdef DEBUG_IDE_ATAPI + { + int i; + printf("ATAPI limit=0x%x packet:", s->lcyl | (s->hcyl << 8)); + for(i = 0; i < ATAPI_PACKET_SIZE; i++) { + printf(" %02x", packet[i]); + } + printf("\n"); + } +#endif + switch(s->io_buffer[0]) { + case GPCMD_TEST_UNIT_READY: + if (bdrv_is_inserted(s->bs)) { + ide_atapi_cmd_ok(s); + } else { + ide_atapi_cmd_error(s, SENSE_NOT_READY, + ASC_MEDIUM_NOT_PRESENT); + } + break; + case GPCMD_MODE_SENSE_10: + { + int action, code; + max_len = ube16_to_cpu(packet + 7); + action = packet[2] >> 6; + code = packet[2] & 0x3f; + switch(action) { + case 0: /* current values */ + switch(code) { + case 0x01: /* error recovery */ + cpu_to_ube16(&buf[0], 16 + 6); + buf[2] = 0x70; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + + buf[8] = 0x01; + buf[9] = 0x06; + buf[10] = 0x00; + buf[11] = 0x05; + buf[12] = 0x00; + buf[13] = 0x00; + buf[14] = 0x00; + buf[15] = 0x00; + ide_atapi_cmd_reply(s, 16, max_len); + break; + case 0x2a: + cpu_to_ube16(&buf[0], 28 + 6); + buf[2] = 0x70; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + + buf[8] = 0x2a; + buf[9] = 0x12; + buf[10] = 0x00; + buf[11] = 0x00; + + buf[12] = 0x70; + buf[13] = 3 << 5; + buf[14] = (1 << 0) | (1 << 3) | (1 << 5); + if (bdrv_is_locked(s->bs)) + buf[6] |= 1 << 1; + buf[15] = 0x00; + cpu_to_ube16(&buf[16], 706); + buf[18] = 0; + buf[19] = 2; + cpu_to_ube16(&buf[20], 512); + cpu_to_ube16(&buf[22], 706); + buf[24] = 0; + buf[25] = 0; + buf[26] = 0; + buf[27] = 0; + ide_atapi_cmd_reply(s, 28, max_len); + break; + default: + goto error_cmd; + } + break; + case 1: /* changeable values */ + goto error_cmd; + case 2: /* default values */ + goto error_cmd; + default: + case 3: /* saved values */ + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_SAVING_PARAMETERS_NOT_SUPPORTED); + break; + } + } + break; + case GPCMD_REQUEST_SENSE: + max_len = packet[4]; + memset(buf, 0, 18); + buf[0] = 0x70 | (1 << 7); + buf[2] = s->sense_key; + buf[7] = 10; + buf[12] = s->asc; + ide_atapi_cmd_reply(s, 18, max_len); + break; + case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + if (bdrv_is_inserted(s->bs)) { + bdrv_set_locked(s->bs, packet[4] & 1); + ide_atapi_cmd_ok(s); + } else { + ide_atapi_cmd_error(s, SENSE_NOT_READY, + ASC_MEDIUM_NOT_PRESENT); + } + break; + case GPCMD_READ_10: + case GPCMD_READ_12: + { + int nb_sectors, lba; + + if (!bdrv_is_inserted(s->bs)) { + ide_atapi_cmd_error(s, SENSE_NOT_READY, + ASC_MEDIUM_NOT_PRESENT); + break; + } + if (packet[0] == GPCMD_READ_10) + nb_sectors = ube16_to_cpu(packet + 7); + else + nb_sectors = ube32_to_cpu(packet + 6); + lba = ube32_to_cpu(packet + 2); + if (nb_sectors == 0) { + ide_atapi_cmd_ok(s); + break; + } + if (((int64_t)(lba + nb_sectors) << 2) > s->nb_sectors) { + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_LOGICAL_BLOCK_OOR); + break; + } + ide_atapi_cmd_read(s, lba, nb_sectors, 2048); + } + break; + case GPCMD_READ_CD: + { + int nb_sectors, lba, transfer_request; + + if (!bdrv_is_inserted(s->bs)) { + ide_atapi_cmd_error(s, SENSE_NOT_READY, + ASC_MEDIUM_NOT_PRESENT); + break; + } + nb_sectors = (packet[6] << 16) | (packet[7] << 8) | packet[8]; + lba = ube32_to_cpu(packet + 2); + if (nb_sectors == 0) { + ide_atapi_cmd_ok(s); + break; + } + if (((int64_t)(lba + nb_sectors) << 2) > s->nb_sectors) { + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_LOGICAL_BLOCK_OOR); + break; + } + transfer_request = packet[9]; + switch(transfer_request & 0xf8) { + case 0x00: + /* nothing */ + ide_atapi_cmd_ok(s); + break; + case 0x10: + /* normal read */ + ide_atapi_cmd_read(s, lba, nb_sectors, 2048); + break; + case 0xf8: + /* read all data */ + ide_atapi_cmd_read(s, lba, nb_sectors, 2352); + break; + default: + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + break; + } + } + break; + case GPCMD_SEEK: + { + int lba; + if (!bdrv_is_inserted(s->bs)) { + ide_atapi_cmd_error(s, SENSE_NOT_READY, + ASC_MEDIUM_NOT_PRESENT); + break; + } + lba = ube32_to_cpu(packet + 2); + if (((int64_t)lba << 2) > s->nb_sectors) { + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_LOGICAL_BLOCK_OOR); + break; + } + ide_atapi_cmd_ok(s); + } + break; + case GPCMD_START_STOP_UNIT: + { + int start, eject; + start = packet[4] & 1; + eject = (packet[4] >> 1) & 1; + + if (eject && !start) { + /* eject the disk */ + bdrv_close(s->bs); + } + ide_atapi_cmd_ok(s); + } + break; + case GPCMD_MECHANISM_STATUS: + { + max_len = ube16_to_cpu(packet + 8); + cpu_to_ube16(buf, 0); + /* no current LBA */ + buf[2] = 0; + buf[3] = 0; + buf[4] = 0; + buf[5] = 1; + cpu_to_ube16(buf + 6, 0); + ide_atapi_cmd_reply(s, 8, max_len); + } + break; + case GPCMD_READ_TOC_PMA_ATIP: + { + int format, msf, start_track, len; + + if (!bdrv_is_inserted(s->bs)) { + ide_atapi_cmd_error(s, SENSE_NOT_READY, + ASC_MEDIUM_NOT_PRESENT); + break; + } + max_len = ube16_to_cpu(packet + 7); + format = packet[9] >> 6; + msf = (packet[1] >> 1) & 1; + start_track = packet[6]; + switch(format) { + case 0: + len = cdrom_read_toc(s, buf, msf, start_track); + if (len < 0) + goto error_cmd; + ide_atapi_cmd_reply(s, len, max_len); + break; + case 1: + /* multi session : only a single session defined */ + memset(buf, 0, 12); + buf[1] = 0x0a; + buf[2] = 0x01; + buf[3] = 0x01; + ide_atapi_cmd_reply(s, 12, max_len); + break; + case 2: + len = cdrom_read_toc_raw(s, buf, msf, start_track); + if (len < 0) + goto error_cmd; + ide_atapi_cmd_reply(s, len, max_len); + break; + default: + error_cmd: + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + break; + } + } + break; + case GPCMD_READ_CDVD_CAPACITY: + if (!bdrv_is_inserted(s->bs)) { + ide_atapi_cmd_error(s, SENSE_NOT_READY, + ASC_MEDIUM_NOT_PRESENT); + break; + } + /* NOTE: it is really the number of sectors minus 1 */ + cpu_to_ube32(buf, (s->nb_sectors >> 2) - 1); + cpu_to_ube32(buf + 4, 2048); + ide_atapi_cmd_reply(s, 8, 8); + break; + case GPCMD_INQUIRY: + max_len = packet[4]; + buf[0] = 0x05; /* CD-ROM */ + buf[1] = 0x80; /* removable */ + buf[2] = 0x00; /* ISO */ + buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */ + buf[4] = 31; /* additionnal length */ + buf[5] = 0; /* reserved */ + buf[6] = 0; /* reserved */ + buf[7] = 0; /* reserved */ + padstr8(buf + 8, 8, "QEMU"); + padstr8(buf + 16, 16, "QEMU CD-ROM"); + padstr8(buf + 32, 4, QEMU_VERSION); + ide_atapi_cmd_reply(s, 36, max_len); + break; + default: + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_ILLEGAL_OPCODE); + break; + } +} + +/* called when the inserted state of the media has changed */ +static void cdrom_change_cb(void *opaque) +{ + IDEState *s = opaque; + int64_t nb_sectors; + + /* XXX: send interrupt too */ + bdrv_get_geometry(s->bs, &nb_sectors); + s->nb_sectors = nb_sectors; +} + +static void ide_cmd_lba48_transform(IDEState *s, int lba48) +{ + s->lba48 = lba48; + + /* handle the 'magic' 0 nsector count conversion here. to avoid + * fiddling with the rest of the read logic, we just store the + * full sector count in ->nsector and ignore ->hob_nsector from now + */ + if (!s->lba48) { + if (!s->nsector) + s->nsector = 256; + } else { + if (!s->nsector && !s->hob_nsector) + s->nsector = 65536; + else { + int lo = s->nsector; + int hi = s->hob_nsector; + + s->nsector = (hi << 8) | lo; + } + } +} + +static void ide_clear_hob(IDEState *ide_if) +{ + /* any write clears HOB high bit of device control register */ + ide_if[0].select &= ~(1 << 7); + ide_if[1].select &= ~(1 << 7); +} + +static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + IDEState *ide_if = opaque; + IDEState *s; + int unit, n; + int lba48 = 0; + +#ifdef DEBUG_IDE + printf("IDE: write addr=0x%x val=0x%02x\n", addr, val); +#endif + + addr &= 7; + switch(addr) { + case 0: + break; + case 1: + ide_clear_hob(ide_if); + /* NOTE: data is written to the two drives */ + ide_if[0].hob_feature = ide_if[0].feature; + ide_if[1].hob_feature = ide_if[1].feature; + ide_if[0].feature = val; + ide_if[1].feature = val; + break; + case 2: + ide_clear_hob(ide_if); + ide_if[0].hob_nsector = ide_if[0].nsector; + ide_if[1].hob_nsector = ide_if[1].nsector; + ide_if[0].nsector = val; + ide_if[1].nsector = val; + break; + case 3: + ide_clear_hob(ide_if); + ide_if[0].hob_sector = ide_if[0].sector; + ide_if[1].hob_sector = ide_if[1].sector; + ide_if[0].sector = val; + ide_if[1].sector = val; + break; + case 4: + ide_clear_hob(ide_if); + ide_if[0].hob_lcyl = ide_if[0].lcyl; + ide_if[1].hob_lcyl = ide_if[1].lcyl; + ide_if[0].lcyl = val; + ide_if[1].lcyl = val; + break; + case 5: + ide_clear_hob(ide_if); + ide_if[0].hob_hcyl = ide_if[0].hcyl; + ide_if[1].hob_hcyl = ide_if[1].hcyl; + ide_if[0].hcyl = val; + ide_if[1].hcyl = val; + break; + case 6: + /* FIXME: HOB readback uses bit 7 */ + ide_if[0].select = (val & ~0x10) | 0xa0; + ide_if[1].select = (val | 0x10) | 0xa0; + /* select drive */ + unit = (val >> 4) & 1; + s = ide_if + unit; + ide_if->cur_drive = s; + break; + default: + case 7: + /* command */ +#if defined(DEBUG_IDE) + printf("ide: CMD=%02x\n", val); +#endif + s = ide_if->cur_drive; + /* ignore commands to non existant slave */ + if (s != ide_if && !s->bs) + break; + + switch(val) { + case WIN_IDENTIFY: + if (s->bs && !s->is_cdrom) { + ide_identify(s); + s->status = READY_STAT | SEEK_STAT; + ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); + } else { + if (s->is_cdrom) { + ide_set_signature(s); + } + ide_abort_command(s); + } + ide_set_irq(s); + break; + case WIN_SPECIFY: + case WIN_RECAL: + s->error = 0; + s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s); + break; + case WIN_SETMULT: + if (s->nsector > MAX_MULT_SECTORS || + s->nsector == 0 || + (s->nsector & (s->nsector - 1)) != 0) { + ide_abort_command(s); + } else { + s->mult_sectors = s->nsector; + s->status = READY_STAT; + } + ide_set_irq(s); + break; + case WIN_VERIFY_EXT: + lba48 = 1; + case WIN_VERIFY: + case WIN_VERIFY_ONCE: + /* do sector number check ? */ + ide_cmd_lba48_transform(s, lba48); + s->status = READY_STAT; + ide_set_irq(s); + break; + case WIN_READ_EXT: + lba48 = 1; + case WIN_READ: + case WIN_READ_ONCE: + if (!s->bs) + goto abort_cmd; + ide_cmd_lba48_transform(s, lba48); + s->req_nb_sectors = 1; + ide_sector_read(s); + break; + case WIN_WRITE_EXT: + lba48 = 1; + case WIN_WRITE: + case WIN_WRITE_ONCE: + ide_cmd_lba48_transform(s, lba48); + s->error = 0; + s->status = SEEK_STAT | READY_STAT; + s->req_nb_sectors = 1; + ide_transfer_start(s, s->io_buffer, 512, ide_sector_write); + break; + case WIN_MULTREAD_EXT: + lba48 = 1; + case WIN_MULTREAD: + if (!s->mult_sectors) + goto abort_cmd; + ide_cmd_lba48_transform(s, lba48); + s->req_nb_sectors = s->mult_sectors; + ide_sector_read(s); + break; + case WIN_MULTWRITE_EXT: + lba48 = 1; + case WIN_MULTWRITE: + if (!s->mult_sectors) + goto abort_cmd; + ide_cmd_lba48_transform(s, lba48); + s->error = 0; + s->status = SEEK_STAT | READY_STAT; + s->req_nb_sectors = s->mult_sectors; + n = s->nsector; + if (n > s->req_nb_sectors) + n = s->req_nb_sectors; + ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write); + break; + case WIN_READDMA_EXT: + lba48 = 1; + case WIN_READDMA: + case WIN_READDMA_ONCE: + if (!s->bs) + goto abort_cmd; + ide_cmd_lba48_transform(s, lba48); + ide_sector_read_dma(s); + break; + case WIN_WRITEDMA_EXT: + lba48 = 1; + case WIN_WRITEDMA: + case WIN_WRITEDMA_ONCE: + if (!s->bs) + goto abort_cmd; + ide_cmd_lba48_transform(s, lba48); + ide_sector_write_dma(s); + break; + case WIN_READ_NATIVE_MAX_EXT: + lba48 = 1; + case WIN_READ_NATIVE_MAX: + ide_cmd_lba48_transform(s, lba48); + ide_set_sector(s, s->nb_sectors - 1); + s->status = READY_STAT; + ide_set_irq(s); + break; + case WIN_CHECKPOWERMODE1: + s->nsector = 0xff; /* device active or idle */ + s->status = READY_STAT; + ide_set_irq(s); + break; + case WIN_SETFEATURES: + if (!s->bs) + goto abort_cmd; + /* XXX: valid for CDROM ? */ + switch(s->feature) { + case 0x02: /* write cache enable */ + case 0x82: /* write cache disable */ + case 0xaa: /* read look-ahead enable */ + case 0x55: /* read look-ahead disable */ + s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s); + break; + case 0x03: { /* set transfer mode */ + uint8_t val = s->nsector & 0x07; + + switch (s->nsector >> 3) { + case 0x00: /* pio default */ + case 0x01: /* pio mode */ + put_le16(s->identify_data + 63,0x07); + put_le16(s->identify_data + 88,0x3f); + break; + case 0x04: /* mdma mode */ + put_le16(s->identify_data + 63,0x07 | (1 << (val + 8))); + put_le16(s->identify_data + 88,0x3f); + break; + case 0x08: /* udma mode */ + put_le16(s->identify_data + 63,0x07); + put_le16(s->identify_data + 88,0x3f | (1 << (val + 8))); + break; + default: + goto abort_cmd; + } + s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s); + break; + } + default: + goto abort_cmd; + } + break; + case WIN_FLUSH_CACHE: + case WIN_FLUSH_CACHE_EXT: + case WIN_STANDBYNOW1: + case WIN_IDLEIMMEDIATE: + s->status = READY_STAT; + ide_set_irq(s); + break; + /* ATAPI commands */ + case WIN_PIDENTIFY: + if (s->is_cdrom) { + ide_atapi_identify(s); + s->status = READY_STAT | SEEK_STAT; + ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); + } else { + ide_abort_command(s); + } + ide_set_irq(s); + break; + case WIN_DIAGNOSE: + ide_set_signature(s); + s->status = 0x00; /* NOTE: READY is _not_ set */ + s->error = 0x01; + break; + case WIN_SRST: + if (!s->is_cdrom) + goto abort_cmd; + ide_set_signature(s); + s->status = 0x00; /* NOTE: READY is _not_ set */ + s->error = 0x01; + break; + case WIN_PACKETCMD: + if (!s->is_cdrom) + goto abort_cmd; + /* overlapping commands not supported */ + if (s->feature & 0x02) + goto abort_cmd; + s->atapi_dma = s->feature & 1; + s->nsector = 1; + ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE, + ide_atapi_cmd); + break; + default: + abort_cmd: + ide_abort_command(s); + ide_set_irq(s); + break; + } + } +} + +static uint32_t ide_ioport_read(void *opaque, uint32_t addr1) +{ + IDEState *ide_if = opaque; + IDEState *s = ide_if->cur_drive; + uint32_t addr; + int ret, hob; + + addr = addr1 & 7; + /* FIXME: HOB readback uses bit 7, but it's always set right now */ + //hob = s->select & (1 << 7); + hob = 0; + switch(addr) { + case 0: + ret = 0xff; + break; + case 1: + if (!ide_if[0].bs && !ide_if[1].bs) + ret = 0; + else if (!hob) + ret = s->error; + else + ret = s->hob_feature; + break; + case 2: + if (!ide_if[0].bs && !ide_if[1].bs) + ret = 0; + else if (!hob) + ret = s->nsector & 0xff; + else + ret = s->hob_nsector; + break; + case 3: + if (!ide_if[0].bs && !ide_if[1].bs) + ret = 0; + else if (!hob) + ret = s->sector; + else + ret = s->hob_sector; + break; + case 4: + if (!ide_if[0].bs && !ide_if[1].bs) + ret = 0; + else if (!hob) + ret = s->lcyl; + else + ret = s->hob_lcyl; + break; + case 5: + if (!ide_if[0].bs && !ide_if[1].bs) + ret = 0; + else if (!hob) + ret = s->hcyl; + else + ret = s->hob_hcyl; + break; + case 6: + if (!ide_if[0].bs && !ide_if[1].bs) + ret = 0; + else + ret = s->select; + break; + default: + case 7: + if ((!ide_if[0].bs && !ide_if[1].bs) || + (s != ide_if && !s->bs)) + ret = 0; + else + ret = s->status; + s->set_irq(s->irq_opaque, s->irq, 0); + break; + } +#ifdef DEBUG_IDE + printf("ide: read addr=0x%x val=%02x\n", addr1, ret); +#endif + return ret; +} + +static uint32_t ide_status_read(void *opaque, uint32_t addr) +{ + IDEState *ide_if = opaque; + IDEState *s = ide_if->cur_drive; + int ret; + + if ((!ide_if[0].bs && !ide_if[1].bs) || + (s != ide_if && !s->bs)) + ret = 0; + else + ret = s->status; +#ifdef DEBUG_IDE + printf("ide: read status addr=0x%x val=%02x\n", addr, ret); +#endif + return ret; +} + +static void ide_cmd_write(void *opaque, uint32_t addr, uint32_t val) +{ + IDEState *ide_if = opaque; + IDEState *s; + int i; + +#ifdef DEBUG_IDE + printf("ide: write control addr=0x%x val=%02x\n", addr, val); +#endif + /* common for both drives */ + if (!(ide_if[0].cmd & IDE_CMD_RESET) && + (val & IDE_CMD_RESET)) { + /* reset low to high */ + for(i = 0;i < 2; i++) { + s = &ide_if[i]; + s->status = BUSY_STAT | SEEK_STAT; + s->error = 0x01; + } + } else if ((ide_if[0].cmd & IDE_CMD_RESET) && + !(val & IDE_CMD_RESET)) { + /* high to low */ + for(i = 0;i < 2; i++) { + s = &ide_if[i]; + if (s->is_cdrom) + s->status = 0x00; /* NOTE: READY is _not_ set */ + else + s->status = READY_STAT | SEEK_STAT; + ide_set_signature(s); + } + } + + ide_if[0].cmd = val; + ide_if[1].cmd = val; +} + +static void ide_data_writew(void *opaque, uint32_t addr, uint32_t val) +{ + IDEState *s = ((IDEState *)opaque)->cur_drive; + uint8_t *p; + + p = s->data_ptr; + *(uint16_t *)p = le16_to_cpu(val); + p += 2; + s->data_ptr = p; + if (p >= s->data_end) + s->end_transfer_func(s); +} + +static uint32_t ide_data_readw(void *opaque, uint32_t addr) +{ + IDEState *s = ((IDEState *)opaque)->cur_drive; + uint8_t *p; + int ret; + p = s->data_ptr; + ret = cpu_to_le16(*(uint16_t *)p); + p += 2; + s->data_ptr = p; + if (p >= s->data_end) + s->end_transfer_func(s); + return ret; +} + +static void ide_data_writel(void *opaque, uint32_t addr, uint32_t val) +{ + IDEState *s = ((IDEState *)opaque)->cur_drive; + uint8_t *p; + + p = s->data_ptr; + *(uint32_t *)p = le32_to_cpu(val); + p += 4; + s->data_ptr = p; + if (p >= s->data_end) + s->end_transfer_func(s); +} + +static uint32_t ide_data_readl(void *opaque, uint32_t addr) +{ + IDEState *s = ((IDEState *)opaque)->cur_drive; + uint8_t *p; + int ret; + + p = s->data_ptr; + ret = cpu_to_le32(*(uint32_t *)p); + p += 4; + s->data_ptr = p; + if (p >= s->data_end) + s->end_transfer_func(s); + return ret; +} + +static void ide_dummy_transfer_stop(IDEState *s) +{ + s->data_ptr = s->io_buffer; + s->data_end = s->io_buffer; + s->io_buffer[0] = 0xff; + s->io_buffer[1] = 0xff; + s->io_buffer[2] = 0xff; + s->io_buffer[3] = 0xff; +} + +static void ide_reset(IDEState *s) +{ + s->mult_sectors = MAX_MULT_SECTORS; + s->cur_drive = s; + s->select = 0xa0; + s->status = READY_STAT; + ide_set_signature(s); + /* init the transfer handler so that 0xffff is returned on data + accesses */ + s->end_transfer_func = ide_dummy_transfer_stop; + ide_dummy_transfer_stop(s); +} + +struct partition { + uint8_t boot_ind; /* 0x80 - active */ + uint8_t head; /* starting head */ + uint8_t sector; /* starting sector */ + uint8_t cyl; /* starting cylinder */ + uint8_t sys_ind; /* What partition type */ + uint8_t end_head; /* end head */ + uint8_t end_sector; /* end sector */ + uint8_t end_cyl; /* end cylinder */ + uint32_t start_sect; /* starting sector counting from 0 */ + uint32_t nr_sects; /* nr of sectors in partition */ +} __attribute__((packed)); + +/* try to guess the disk logical geometry from the MSDOS partition table. Return 0 if OK, -1 if could not guess */ +static int guess_disk_lchs(IDEState *s, + int *pcylinders, int *pheads, int *psectors) +{ + uint8_t buf[512]; + int ret, i, heads, sectors, cylinders; + struct partition *p; + uint32_t nr_sects; + + ret = bdrv_read(s->bs, 0, buf, 1); + if (ret < 0) + return -1; + /* test msdos magic */ + if (buf[510] != 0x55 || buf[511] != 0xaa) + return -1; + for(i = 0; i < 4; i++) { + p = ((struct partition *)(buf + 0x1be)) + i; + nr_sects = le32_to_cpu(p->nr_sects); + if (nr_sects && p->end_head) { + /* We make the assumption that the partition terminates on + a cylinder boundary */ + heads = p->end_head + 1; + sectors = p->end_sector & 63; + if (sectors == 0) + continue; + cylinders = s->nb_sectors / (heads * sectors); + if (cylinders < 1 || cylinders > 16383) + continue; + *pheads = heads; + *psectors = sectors; + *pcylinders = cylinders; +#if 0 + printf("guessed geometry: LCHS=%d %d %d\n", + cylinders, heads, sectors); +#endif + return 0; + } + } + return -1; +} + +static void ide_init2(IDEState *ide_state, + BlockDriverState *hd0, BlockDriverState *hd1, + SetIRQFunc *set_irq, void *irq_opaque, int irq) +{ + IDEState *s; + static int drive_serial = 1; + int i, cylinders, heads, secs, translation; + int64_t nb_sectors; + + for(i = 0; i < 2; i++) { + s = ide_state + i; + if (i == 0) + s->bs = hd0; + else + s->bs = hd1; + if (s->bs) { + bdrv_get_geometry(s->bs, &nb_sectors); + s->nb_sectors = nb_sectors; + /* if a geometry hint is available, use it */ + bdrv_get_geometry_hint(s->bs, &cylinders, &heads, &secs); + if (cylinders != 0) { + s->cylinders = cylinders; + s->heads = heads; + s->sectors = secs; + } else { + if (guess_disk_lchs(s, &cylinders, &heads, &secs) == 0) { + if (heads > 16) { + /* if heads > 16, it means that a BIOS LBA + translation was active, so the default + hardware geometry is OK */ + goto default_geometry; + } else { + s->cylinders = cylinders; + s->heads = heads; + s->sectors = secs; + /* disable any translation to be in sync with + the logical geometry */ + translation = bdrv_get_translation_hint(s->bs); + if (translation == BIOS_ATA_TRANSLATION_AUTO) { + bdrv_set_translation_hint(s->bs, + BIOS_ATA_TRANSLATION_NONE); + } + } + } else { + default_geometry: + /* if no geometry, use a standard physical disk geometry */ + cylinders = nb_sectors / (16 * 63); + if (cylinders > 16383) + cylinders = 16383; + else if (cylinders < 2) + cylinders = 2; + s->cylinders = cylinders; + s->heads = 16; + s->sectors = 63; + } + bdrv_set_geometry_hint(s->bs, s->cylinders, s->heads, s->sectors); + } + if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { + s->is_cdrom = 1; + bdrv_set_change_cb(s->bs, cdrom_change_cb, s); + } + } + s->drive_serial = drive_serial++; + s->set_irq = set_irq; + s->irq_opaque = irq_opaque; + s->irq = irq; + s->sector_write_timer = qemu_new_timer(vm_clock, + ide_sector_write_timer_cb, s); + ide_reset(s); + } +} + +static void ide_init_ioport(IDEState *ide_state, int iobase, int iobase2) +{ + register_ioport_write(iobase, 8, 1, ide_ioport_write, ide_state); + register_ioport_read(iobase, 8, 1, ide_ioport_read, ide_state); + if (iobase2) { + register_ioport_read(iobase2, 1, 1, ide_status_read, ide_state); + register_ioport_write(iobase2, 1, 1, ide_cmd_write, ide_state); + } + + /* data ports */ + register_ioport_write(iobase, 2, 2, ide_data_writew, ide_state); + register_ioport_read(iobase, 2, 2, ide_data_readw, ide_state); + register_ioport_write(iobase, 4, 4, ide_data_writel, ide_state); + register_ioport_read(iobase, 4, 4, ide_data_readl, ide_state); +} + +/***********************************************************/ +/* ISA IDE definitions */ + +void isa_ide_init(int iobase, int iobase2, int irq, + BlockDriverState *hd0, BlockDriverState *hd1) +{ + IDEState *ide_state; + + ide_state = qemu_mallocz(sizeof(IDEState) * 2); + if (!ide_state) + return; + + ide_init2(ide_state, hd0, hd1, pic_set_irq_new, isa_pic, irq); + ide_init_ioport(ide_state, iobase, iobase2); +} + +/***********************************************************/ +/* PCI IDE definitions */ + +static void cmd646_update_irq(PCIIDEState *d); + +static void ide_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + PCIIDEState *d = (PCIIDEState *)pci_dev; + IDEState *ide_state; + + if (region_num <= 3) { + ide_state = &d->ide_if[(region_num >> 1) * 2]; + if (region_num & 1) { + register_ioport_read(addr + 2, 1, 1, ide_status_read, ide_state); + register_ioport_write(addr + 2, 1, 1, ide_cmd_write, ide_state); + } else { + register_ioport_write(addr, 8, 1, ide_ioport_write, ide_state); + register_ioport_read(addr, 8, 1, ide_ioport_read, ide_state); + + /* data ports */ + register_ioport_write(addr, 2, 2, ide_data_writew, ide_state); + register_ioport_read(addr, 2, 2, ide_data_readw, ide_state); + register_ioport_write(addr, 4, 4, ide_data_writel, ide_state); + register_ioport_read(addr, 4, 4, ide_data_readl, ide_state); + } + } +} + +static void ide_dma_finish(BMDMAState *bm) +{ + IDEState *s = bm->ide_if; + + bm->status &= ~BM_STATUS_DMAING; + bm->status |= BM_STATUS_INT; + bm->dma_cb = NULL; + bm->ide_if = NULL; +#ifdef DMA_MULTI_THREAD + ide_set_irq(s); +#endif /* DMA_MULTI_THREAD */ +} + +/* XXX: full callback usage to prepare non blocking I/Os support - + error handling */ +#ifdef DMA_MULTI_THREAD +static void ide_dma_loop(BMDMAState *bm) +{ + write(file_pipes[1], &bm, sizeof(bm)); +} +static void dma_thread_loop(BMDMAState *bm) +#else /* DMA_MULTI_THREAD */ +static void ide_dma_loop(BMDMAState *bm) +#endif /* !DMA_MULTI_THREAD */ +{ + struct { + uint32_t addr; + uint32_t size; + } prd; + target_phys_addr_t cur_addr; + int len, i, len1; + + cur_addr = bm->addr; + /* at most one page to avoid hanging if erroneous parameters */ + for(i = 0; i < 512; i++) { + cpu_physical_memory_read(cur_addr, (uint8_t *)&prd, 8); + prd.addr = le32_to_cpu(prd.addr); + prd.size = le32_to_cpu(prd.size); +#ifdef DEBUG_IDE + printf("ide: dma: prd: %08x: addr=0x%08x size=0x%08x\n", + (int)cur_addr, prd.addr, prd.size); +#endif + len = prd.size & 0xfffe; + if (len == 0) + len = 0x10000; + while (len > 0) { + len1 = bm->dma_cb(bm->ide_if, prd.addr, len); + if (len1 == 0) + goto the_end; + prd.addr += len1; + len -= len1; + } + /* end of transfer */ + if (prd.size & 0x80000000) + break; + cur_addr += 8; + } + /* end of transfer */ + the_end: + ide_dma_finish(bm); +} + +static void ide_dma_start(IDEState *s, IDEDMAFunc *dma_cb) +{ + BMDMAState *bm = s->bmdma; + if(!bm) + return; + bm->ide_if = s; + bm->dma_cb = dma_cb; + if (bm->status & BM_STATUS_DMAING) { + ide_dma_loop(bm); + } +} + +static void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + BMDMAState *bm = opaque; +#ifdef DEBUG_IDE + printf("%s: 0x%08x\n", __func__, val); +#endif + if (!(val & BM_CMD_START)) { + /* XXX: do it better */ + bm->status &= ~BM_STATUS_DMAING; + bm->cmd = val & 0x09; + } else { + bm->status |= BM_STATUS_DMAING; + bm->cmd = val & 0x09; + /* start dma transfer if possible */ + if (bm->dma_cb) + ide_dma_loop(bm); + } +} + +static uint32_t bmdma_readb(void *opaque, uint32_t addr) +{ + BMDMAState *bm = opaque; + PCIIDEState *pci_dev; + uint32_t val; + + switch(addr & 3) { + case 0: + val = bm->cmd; + break; + case 1: + pci_dev = bm->pci_dev; + if (pci_dev->type == IDE_TYPE_CMD646) { + val = pci_dev->dev.config[MRDMODE]; + } else { + val = 0xff; + } + break; + case 2: + val = bm->status; + break; + case 3: + pci_dev = bm->pci_dev; + if (pci_dev->type == IDE_TYPE_CMD646) { + if (bm == &pci_dev->bmdma[0]) + val = pci_dev->dev.config[UDIDETCR0]; + else + val = pci_dev->dev.config[UDIDETCR1]; + } else { + val = 0xff; + } + break; + default: + val = 0xff; + break; + } +#ifdef DEBUG_IDE + printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val); +#endif + return val; +} + +static void bmdma_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + BMDMAState *bm = opaque; + PCIIDEState *pci_dev; +#ifdef DEBUG_IDE + printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val); +#endif + switch(addr & 3) { + case 1: + pci_dev = bm->pci_dev; + if (pci_dev->type == IDE_TYPE_CMD646) { + pci_dev->dev.config[MRDMODE] = + (pci_dev->dev.config[MRDMODE] & ~0x30) | (val & 0x30); + cmd646_update_irq(pci_dev); + } + break; + case 2: + bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06); + break; + case 3: + pci_dev = bm->pci_dev; + if (pci_dev->type == IDE_TYPE_CMD646) { + if (bm == &pci_dev->bmdma[0]) + pci_dev->dev.config[UDIDETCR0] = val; + else + pci_dev->dev.config[UDIDETCR1] = val; + } + break; + } +} + +static uint32_t bmdma_addr_readl(void *opaque, uint32_t addr) +{ + BMDMAState *bm = opaque; + uint32_t val; + val = bm->addr; +#ifdef DEBUG_IDE + printf("%s: 0x%08x\n", __func__, val); +#endif + return val; +} + +static void bmdma_addr_writel(void *opaque, uint32_t addr, uint32_t val) +{ + BMDMAState *bm = opaque; +#ifdef DEBUG_IDE + printf("%s: 0x%08x\n", __func__, val); +#endif + bm->addr = val & ~3; +} + +static void bmdma_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + PCIIDEState *d = (PCIIDEState *)pci_dev; + int i; + + for(i = 0;i < 2; i++) { + BMDMAState *bm = &d->bmdma[i]; + d->ide_if[2 * i].bmdma = bm; + d->ide_if[2 * i + 1].bmdma = bm; + bm->pci_dev = (PCIIDEState *)pci_dev; + + register_ioport_write(addr, 1, 1, bmdma_cmd_writeb, bm); + + register_ioport_write(addr + 1, 3, 1, bmdma_writeb, bm); + register_ioport_read(addr, 4, 1, bmdma_readb, bm); + + register_ioport_write(addr + 4, 4, 4, bmdma_addr_writel, bm); + register_ioport_read(addr + 4, 4, 4, bmdma_addr_readl, bm); + addr += 8; + } +} + +/* XXX: call it also when the MRDMODE is changed from the PCI config + registers */ +static void cmd646_update_irq(PCIIDEState *d) +{ + int pci_level; + pci_level = ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH0) && + !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH0)) || + ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH1) && + !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH1)); + pci_set_irq((PCIDevice *)d, 0, pci_level); +} + +/* the PCI irq level is the logical OR of the two channels */ +static void cmd646_set_irq(void *opaque, int channel, int level) +{ + PCIIDEState *d = opaque; + int irq_mask; + + irq_mask = MRDMODE_INTR_CH0 << channel; + if (level) + d->dev.config[MRDMODE] |= irq_mask; + else + d->dev.config[MRDMODE] &= ~irq_mask; + cmd646_update_irq(d); +} + +/* CMD646 PCI IDE controller */ +void pci_cmd646_ide_init(PCIBus *bus, BlockDriverState **hd_table, + int secondary_ide_enabled) +{ + PCIIDEState *d; + uint8_t *pci_conf; + int i; + + d = (PCIIDEState *)pci_register_device(bus, "CMD646 IDE", + sizeof(PCIIDEState), + -1, + NULL, NULL); + d->type = IDE_TYPE_CMD646; + pci_conf = d->dev.config; + pci_conf[0x00] = 0x95; // CMD646 + pci_conf[0x01] = 0x10; + pci_conf[0x02] = 0x46; + pci_conf[0x03] = 0x06; + + pci_conf[0x08] = 0x07; // IDE controller revision + pci_conf[0x09] = 0x8f; + + pci_conf[0x0a] = 0x01; // class_sub = PCI_IDE + pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage + pci_conf[0x0e] = 0x00; // header_type + + if (secondary_ide_enabled) { + /* XXX: if not enabled, really disable the seconday IDE controller */ + pci_conf[0x51] = 0x80; /* enable IDE1 */ + } + + pci_register_io_region((PCIDevice *)d, 0, 0x8, + PCI_ADDRESS_SPACE_IO, ide_map); + pci_register_io_region((PCIDevice *)d, 1, 0x4, + PCI_ADDRESS_SPACE_IO, ide_map); + pci_register_io_region((PCIDevice *)d, 2, 0x8, + PCI_ADDRESS_SPACE_IO, ide_map); + pci_register_io_region((PCIDevice *)d, 3, 0x4, + PCI_ADDRESS_SPACE_IO, ide_map); + pci_register_io_region((PCIDevice *)d, 4, 0x10, + PCI_ADDRESS_SPACE_IO, bmdma_map); + + pci_conf[0x3d] = 0x01; // interrupt on pin 1 + + for(i = 0; i < 4; i++) + d->ide_if[i].pci_dev = (PCIDevice *)d; + ide_init2(&d->ide_if[0], hd_table[0], hd_table[1], + cmd646_set_irq, d, 0); + ide_init2(&d->ide_if[2], hd_table[2], hd_table[3], + cmd646_set_irq, d, 1); +#ifdef DMA_MULTI_THREAD + dma_create_thread(); +#endif /* DMA_MULTI_THREAD */ +} + +/* hd_table must contain 4 block drivers */ +/* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */ +void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table) +{ + PCIIDEState *d; + uint8_t *pci_conf; + + /* register a function 1 of PIIX3 */ + d = (PCIIDEState *)pci_register_device(bus, "PIIX3 IDE", + sizeof(PCIIDEState), + ((PCIDevice *)piix3_state)->devfn + 1, + NULL, NULL); + d->type = IDE_TYPE_PIIX3; + + pci_conf = d->dev.config; + pci_conf[0x00] = 0x86; // Intel + pci_conf[0x01] = 0x80; + pci_conf[0x02] = 0x10; + pci_conf[0x03] = 0x70; + pci_conf[0x09] = 0x80; // legacy ATA mode + pci_conf[0x0a] = 0x01; // class_sub = PCI_IDE + pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage + pci_conf[0x0e] = 0x00; // header_type + + pci_register_io_region((PCIDevice *)d, 4, 0x10, + PCI_ADDRESS_SPACE_IO, bmdma_map); + + ide_init2(&d->ide_if[0], hd_table[0], hd_table[1], + pic_set_irq_new, isa_pic, 14); + ide_init2(&d->ide_if[2], hd_table[2], hd_table[3], + pic_set_irq_new, isa_pic, 15); + ide_init_ioport(&d->ide_if[0], 0x1f0, 0x3f6); + ide_init_ioport(&d->ide_if[2], 0x170, 0x376); +#ifdef DMA_MULTI_THREAD + dma_create_thread(); +#endif //DMA_MULTI_THREAD +} + +/***********************************************************/ +/* MacIO based PowerPC IDE */ + +/* PowerMac IDE memory IO */ +static void pmac_ide_writeb (void *opaque, + target_phys_addr_t addr, uint32_t val) +{ + addr = (addr & 0xFFF) >> 4; + switch (addr) { + case 1 ... 7: + ide_ioport_write(opaque, addr, val); + break; + case 8: + case 22: + ide_cmd_write(opaque, 0, val); + break; + default: + break; + } +} + +static uint32_t pmac_ide_readb (void *opaque,target_phys_addr_t addr) +{ + uint8_t retval; + + addr = (addr & 0xFFF) >> 4; + switch (addr) { + case 1 ... 7: + retval = ide_ioport_read(opaque, addr); + break; + case 8: + case 22: + retval = ide_status_read(opaque, 0); + break; + default: + retval = 0xFF; + break; + } + return retval; +} + +static void pmac_ide_writew (void *opaque, + target_phys_addr_t addr, uint32_t val) +{ + addr = (addr & 0xFFF) >> 4; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap16(val); +#endif + if (addr == 0) { + ide_data_writew(opaque, 0, val); + } +} + +static uint32_t pmac_ide_readw (void *opaque,target_phys_addr_t addr) +{ + uint16_t retval; + + addr = (addr & 0xFFF) >> 4; + if (addr == 0) { + retval = ide_data_readw(opaque, 0); + } else { + retval = 0xFFFF; + } +#ifdef TARGET_WORDS_BIGENDIAN + retval = bswap16(retval); +#endif + return retval; +} + +static void pmac_ide_writel (void *opaque, + target_phys_addr_t addr, uint32_t val) +{ + addr = (addr & 0xFFF) >> 4; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + if (addr == 0) { + ide_data_writel(opaque, 0, val); + } +} + +static uint32_t pmac_ide_readl (void *opaque,target_phys_addr_t addr) +{ + uint32_t retval; + + addr = (addr & 0xFFF) >> 4; + if (addr == 0) { + retval = ide_data_readl(opaque, 0); + } else { + retval = 0xFFFFFFFF; + } +#ifdef TARGET_WORDS_BIGENDIAN + retval = bswap32(retval); +#endif + return retval; +} + +static CPUWriteMemoryFunc *pmac_ide_write[] = { + pmac_ide_writeb, + pmac_ide_writew, + pmac_ide_writel, +}; + +static CPUReadMemoryFunc *pmac_ide_read[] = { + pmac_ide_readb, + pmac_ide_readw, + pmac_ide_readl, +}; + +/* hd_table must contain 4 block drivers */ +/* PowerMac uses memory mapped registers, not I/O. Return the memory + I/O index to access the ide. */ +int pmac_ide_init (BlockDriverState **hd_table, + SetIRQFunc *set_irq, void *irq_opaque, int irq) +{ + IDEState *ide_if; + int pmac_ide_memory; + + ide_if = qemu_mallocz(sizeof(IDEState) * 2); + ide_init2(&ide_if[0], hd_table[0], hd_table[1], + set_irq, irq_opaque, irq); + + pmac_ide_memory = cpu_register_io_memory(0, pmac_ide_read, + pmac_ide_write, &ide_if[0]); + return pmac_ide_memory; +} diff --git a/tools/ioemu/hw/integratorcp.c b/tools/ioemu/hw/integratorcp.c new file mode 100644 index 0000000000..f438af733d --- /dev/null +++ b/tools/ioemu/hw/integratorcp.c @@ -0,0 +1,546 @@ +/* + * ARM Integrator CP System emulation. + * + * Copyright (c) 2005-2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GPL + */ + +#include "vl.h" +#include "arm_pic.h" + +void DMA_run (void) +{ +} + +typedef struct { + uint32_t flash_offset; + uint32_t cm_osc; + uint32_t cm_ctrl; + uint32_t cm_lock; + uint32_t cm_auxosc; + uint32_t cm_sdram; + uint32_t cm_init; + uint32_t cm_flags; + uint32_t cm_nvflags; + uint32_t int_level; + uint32_t irq_enabled; + uint32_t fiq_enabled; +} integratorcm_state; + +static uint8_t integrator_spd[128] = { + 128, 8, 4, 11, 9, 1, 64, 0, 2, 0xa0, 0xa0, 0, 0, 8, 0, 1, + 0xe, 4, 0x1c, 1, 2, 0x20, 0xc0, 0, 0, 0, 0, 0x30, 0x28, 0x30, 0x28, 0x40 +}; + +static uint32_t integratorcm_read(void *opaque, target_phys_addr_t offset) +{ + integratorcm_state *s = (integratorcm_state *)opaque; + offset -= 0x10000000; + if (offset >= 0x100 && offset < 0x200) { + /* CM_SPD */ + if (offset >= 0x180) + return 0; + return integrator_spd[offset >> 2]; + } + switch (offset >> 2) { + case 0: /* CM_ID */ + return 0x411a3001; + case 1: /* CM_PROC */ + return 0; + case 2: /* CM_OSC */ + return s->cm_osc; + case 3: /* CM_CTRL */ + return s->cm_ctrl; + case 4: /* CM_STAT */ + return 0x00100000; + case 5: /* CM_LOCK */ + if (s->cm_lock == 0xa05f) { + return 0x1a05f; + } else { + return s->cm_lock; + } + case 6: /* CM_LMBUSCNT */ + /* ??? High frequency timer. */ + cpu_abort(cpu_single_env, "integratorcm_read: CM_LMBUSCNT"); + case 7: /* CM_AUXOSC */ + return s->cm_auxosc; + case 8: /* CM_SDRAM */ + return s->cm_sdram; + case 9: /* CM_INIT */ + return s->cm_init; + case 10: /* CM_REFCT */ + /* ??? High frequency timer. */ + cpu_abort(cpu_single_env, "integratorcm_read: CM_REFCT"); + case 12: /* CM_FLAGS */ + return s->cm_flags; + case 14: /* CM_NVFLAGS */ + return s->cm_nvflags; + case 16: /* CM_IRQ_STAT */ + return s->int_level & s->irq_enabled; + case 17: /* CM_IRQ_RSTAT */ + return s->int_level; + case 18: /* CM_IRQ_ENSET */ + return s->irq_enabled; + case 20: /* CM_SOFT_INTSET */ + return s->int_level & 1; + case 24: /* CM_FIQ_STAT */ + return s->int_level & s->fiq_enabled; + case 25: /* CM_FIQ_RSTAT */ + return s->int_level; + case 26: /* CM_FIQ_ENSET */ + return s->fiq_enabled; + case 32: /* CM_VOLTAGE_CTL0 */ + case 33: /* CM_VOLTAGE_CTL1 */ + case 34: /* CM_VOLTAGE_CTL2 */ + case 35: /* CM_VOLTAGE_CTL3 */ + /* ??? Voltage control unimplemented. */ + return 0; + default: + cpu_abort (cpu_single_env, + "integratorcm_read: Unimplemented offset 0x%x\n", offset); + return 0; + } +} + +static void integratorcm_do_remap(integratorcm_state *s, int flash) +{ + if (flash) { + cpu_register_physical_memory(0, 0x100000, IO_MEM_RAM); + } else { + cpu_register_physical_memory(0, 0x100000, s->flash_offset | IO_MEM_RAM); + } + //??? tlb_flush (cpu_single_env, 1); +} + +static void integratorcm_set_ctrl(integratorcm_state *s, uint32_t value) +{ + if (value & 8) { + cpu_abort(cpu_single_env, "Board reset\n"); + } + if ((s->cm_init ^ value) & 4) { + integratorcm_do_remap(s, (value & 4) == 0); + } + if ((s->cm_init ^ value) & 1) { + printf("Green LED %s\n", (value & 1) ? "on" : "off"); + } + s->cm_init = (s->cm_init & ~ 5) | (value ^ 5); +} + +static void integratorcm_update(integratorcm_state *s) +{ + /* ??? The CPU irq/fiq is raised when either the core module or base PIC + are active. */ + if (s->int_level & (s->irq_enabled | s->fiq_enabled)) + cpu_abort(cpu_single_env, "Core module interrupt\n"); +} + +static void integratorcm_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + integratorcm_state *s = (integratorcm_state *)opaque; + offset -= 0x10000000; + switch (offset >> 2) { + case 2: /* CM_OSC */ + if (s->cm_lock == 0xa05f) + s->cm_osc = value; + break; + case 3: /* CM_CTRL */ + integratorcm_set_ctrl(s, value); + break; + case 5: /* CM_LOCK */ + s->cm_lock = value & 0xffff; + break; + case 7: /* CM_AUXOSC */ + if (s->cm_lock == 0xa05f) + s->cm_auxosc = value; + break; + case 8: /* CM_SDRAM */ + s->cm_sdram = value; + break; + case 9: /* CM_INIT */ + /* ??? This can change the memory bus frequency. */ + s->cm_init = value; + break; + case 12: /* CM_FLAGSS */ + s->cm_flags |= value; + break; + case 13: /* CM_FLAGSC */ + s->cm_flags &= ~value; + break; + case 14: /* CM_NVFLAGSS */ + s->cm_nvflags |= value; + break; + case 15: /* CM_NVFLAGSS */ + s->cm_nvflags &= ~value; + break; + case 18: /* CM_IRQ_ENSET */ + s->irq_enabled |= value; + integratorcm_update(s); + break; + case 19: /* CM_IRQ_ENCLR */ + s->irq_enabled &= ~value; + integratorcm_update(s); + break; + case 20: /* CM_SOFT_INTSET */ + s->int_level |= (value & 1); + integratorcm_update(s); + break; + case 21: /* CM_SOFT_INTCLR */ + s->int_level &= ~(value & 1); + integratorcm_update(s); + break; + case 26: /* CM_FIQ_ENSET */ + s->fiq_enabled |= value; + integratorcm_update(s); + break; + case 27: /* CM_FIQ_ENCLR */ + s->fiq_enabled &= ~value; + integratorcm_update(s); + break; + case 32: /* CM_VOLTAGE_CTL0 */ + case 33: /* CM_VOLTAGE_CTL1 */ + case 34: /* CM_VOLTAGE_CTL2 */ + case 35: /* CM_VOLTAGE_CTL3 */ + /* ??? Voltage control unimplemented. */ + break; + default: + cpu_abort (cpu_single_env, + "integratorcm_write: Unimplemented offset 0x%x\n", offset); + break; + } +} + +/* Integrator/CM control registers. */ + +static CPUReadMemoryFunc *integratorcm_readfn[] = { + integratorcm_read, + integratorcm_read, + integratorcm_read +}; + +static CPUWriteMemoryFunc *integratorcm_writefn[] = { + integratorcm_write, + integratorcm_write, + integratorcm_write +}; + +static void integratorcm_init(int memsz, uint32_t flash_offset) +{ + int iomemtype; + integratorcm_state *s; + + s = (integratorcm_state *)qemu_mallocz(sizeof(integratorcm_state)); + s->cm_osc = 0x01000048; + /* ??? What should the high bits of this value be? */ + s->cm_auxosc = 0x0007feff; + s->cm_sdram = 0x00011122; + if (memsz >= 256) { + integrator_spd[31] = 64; + s->cm_sdram |= 0x10; + } else if (memsz >= 128) { + integrator_spd[31] = 32; + s->cm_sdram |= 0x0c; + } else if (memsz >= 64) { + integrator_spd[31] = 16; + s->cm_sdram |= 0x08; + } else if (memsz >= 32) { + integrator_spd[31] = 4; + s->cm_sdram |= 0x04; + } else { + integrator_spd[31] = 2; + } + memcpy(integrator_spd + 73, "QEMU-MEMORY", 11); + s->cm_init = 0x00000112; + s->flash_offset = flash_offset; + + iomemtype = cpu_register_io_memory(0, integratorcm_readfn, + integratorcm_writefn, s); + cpu_register_physical_memory(0x10000000, 0x007fffff, iomemtype); + integratorcm_do_remap(s, 1); + /* ??? Save/restore. */ +} + +/* Integrator/CP hardware emulation. */ +/* Primary interrupt controller. */ + +typedef struct icp_pic_state +{ + arm_pic_handler handler; + uint32_t base; + uint32_t level; + uint32_t irq_enabled; + uint32_t fiq_enabled; + void *parent; + int parent_irq; + int parent_fiq; +} icp_pic_state; + +static void icp_pic_update(icp_pic_state *s) +{ + uint32_t flags; + + if (s->parent_irq != -1) { + flags = (s->level & s->irq_enabled); + pic_set_irq_new(s->parent, s->parent_irq, flags != 0); + } + if (s->parent_fiq != -1) { + flags = (s->level & s->fiq_enabled); + pic_set_irq_new(s->parent, s->parent_fiq, flags != 0); + } +} + +static void icp_pic_set_irq(void *opaque, int irq, int level) +{ + icp_pic_state *s = (icp_pic_state *)opaque; + if (level) + s->level |= 1 << irq; + else + s->level &= ~(1 << irq); + icp_pic_update(s); +} + +static uint32_t icp_pic_read(void *opaque, target_phys_addr_t offset) +{ + icp_pic_state *s = (icp_pic_state *)opaque; + + offset -= s->base; + switch (offset >> 2) { + case 0: /* IRQ_STATUS */ + return s->level & s->irq_enabled; + case 1: /* IRQ_RAWSTAT */ + return s->level; + case 2: /* IRQ_ENABLESET */ + return s->irq_enabled; + case 4: /* INT_SOFTSET */ + return s->level & 1; + case 8: /* FRQ_STATUS */ + return s->level & s->fiq_enabled; + case 9: /* FRQ_RAWSTAT */ + return s->level; + case 10: /* FRQ_ENABLESET */ + return s->fiq_enabled; + case 3: /* IRQ_ENABLECLR */ + case 5: /* INT_SOFTCLR */ + case 11: /* FRQ_ENABLECLR */ + default: + printf ("icp_pic_read: Bad register offset 0x%x\n", offset); + return 0; + } +} + +static void icp_pic_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + icp_pic_state *s = (icp_pic_state *)opaque; + offset -= s->base; + + switch (offset >> 2) { + case 2: /* IRQ_ENABLESET */ + s->irq_enabled |= value; + break; + case 3: /* IRQ_ENABLECLR */ + s->irq_enabled &= ~value; + break; + case 4: /* INT_SOFTSET */ + if (value & 1) + pic_set_irq_new(s, 0, 1); + break; + case 5: /* INT_SOFTCLR */ + if (value & 1) + pic_set_irq_new(s, 0, 0); + break; + case 10: /* FRQ_ENABLESET */ + s->fiq_enabled |= value; + break; + case 11: /* FRQ_ENABLECLR */ + s->fiq_enabled &= ~value; + break; + case 0: /* IRQ_STATUS */ + case 1: /* IRQ_RAWSTAT */ + case 8: /* FRQ_STATUS */ + case 9: /* FRQ_RAWSTAT */ + default: + printf ("icp_pic_write: Bad register offset 0x%x\n", offset); + return; + } + icp_pic_update(s); +} + +static CPUReadMemoryFunc *icp_pic_readfn[] = { + icp_pic_read, + icp_pic_read, + icp_pic_read +}; + +static CPUWriteMemoryFunc *icp_pic_writefn[] = { + icp_pic_write, + icp_pic_write, + icp_pic_write +}; + +static icp_pic_state *icp_pic_init(uint32_t base, void *parent, + int parent_irq, int parent_fiq) +{ + icp_pic_state *s; + int iomemtype; + + s = (icp_pic_state *)qemu_mallocz(sizeof(icp_pic_state)); + if (!s) + return NULL; + s->handler = icp_pic_set_irq; + s->base = base; + s->parent = parent; + s->parent_irq = parent_irq; + s->parent_fiq = parent_fiq; + iomemtype = cpu_register_io_memory(0, icp_pic_readfn, + icp_pic_writefn, s); + cpu_register_physical_memory(base, 0x007fffff, iomemtype); + /* ??? Save/restore. */ + return s; +} + +/* CP control registers. */ +typedef struct { + uint32_t base; +} icp_control_state; + +static uint32_t icp_control_read(void *opaque, target_phys_addr_t offset) +{ + icp_control_state *s = (icp_control_state *)opaque; + offset -= s->base; + switch (offset >> 2) { + case 0: /* CP_IDFIELD */ + return 0x41034003; + case 1: /* CP_FLASHPROG */ + return 0; + case 2: /* CP_INTREG */ + return 0; + case 3: /* CP_DECODE */ + return 0x11; + default: + cpu_abort (cpu_single_env, "icp_control_read: Bad offset %x\n", offset); + return 0; + } +} + +static void icp_control_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + icp_control_state *s = (icp_control_state *)opaque; + offset -= s->base; + switch (offset >> 2) { + case 1: /* CP_FLASHPROG */ + case 2: /* CP_INTREG */ + case 3: /* CP_DECODE */ + /* Nothing interesting implemented yet. */ + break; + default: + cpu_abort (cpu_single_env, "icp_control_write: Bad offset %x\n", offset); + } +} +static CPUReadMemoryFunc *icp_control_readfn[] = { + icp_control_read, + icp_control_read, + icp_control_read +}; + +static CPUWriteMemoryFunc *icp_control_writefn[] = { + icp_control_write, + icp_control_write, + icp_control_write +}; + +static void icp_control_init(uint32_t base) +{ + int iomemtype; + icp_control_state *s; + + s = (icp_control_state *)qemu_mallocz(sizeof(icp_control_state)); + iomemtype = cpu_register_io_memory(0, icp_control_readfn, + icp_control_writefn, s); + cpu_register_physical_memory(base, 0x007fffff, iomemtype); + s->base = base; + /* ??? Save/restore. */ +} + + +/* Board init. */ + +static void integratorcp_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, uint32_t cpuid) +{ + CPUState *env; + uint32_t bios_offset; + icp_pic_state *pic; + void *cpu_pic; + + env = cpu_init(); + cpu_arm_set_model(env, cpuid); + bios_offset = ram_size + vga_ram_size; + /* ??? On a real system the first 1Mb is mapped as SSRAM or boot flash. */ + /* ??? RAM shoud repeat to fill physical memory space. */ + /* SDRAM at address zero*/ + cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); + /* And again at address 0x80000000 */ + cpu_register_physical_memory(0x80000000, ram_size, IO_MEM_RAM); + + integratorcm_init(ram_size >> 20, bios_offset); + cpu_pic = arm_pic_init_cpu(env); + pic = icp_pic_init(0x14000000, cpu_pic, ARM_PIC_CPU_IRQ, ARM_PIC_CPU_FIQ); + icp_pic_init(0xca000000, pic, 26, -1); + icp_pit_init(0x13000000, pic, 5); + pl011_init(0x16000000, pic, 1, serial_hds[0]); + pl011_init(0x17000000, pic, 2, serial_hds[1]); + icp_control_init(0xcb000000); + pl050_init(0x18000000, pic, 3, 0); + pl050_init(0x19000000, pic, 4, 1); + if (nd_table[0].vlan) { + if (nd_table[0].model == NULL + || strcmp(nd_table[0].model, "smc91c111") == 0) { + smc91c111_init(&nd_table[0], 0xc8000000, pic, 27); + } else { + fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model); + exit (1); + } + } + pl110_init(ds, 0xc0000000, pic, 22, 0); + + arm_load_kernel(ram_size, kernel_filename, kernel_cmdline, + initrd_filename, 0x113); +} + +static void integratorcp926_init(int ram_size, int vga_ram_size, + int boot_device, DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + integratorcp_init(ram_size, vga_ram_size, boot_device, ds, fd_filename, + snapshot, kernel_filename, kernel_cmdline, + initrd_filename, ARM_CPUID_ARM926); +} + +static void integratorcp1026_init(int ram_size, int vga_ram_size, + int boot_device, DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + integratorcp_init(ram_size, vga_ram_size, boot_device, ds, fd_filename, + snapshot, kernel_filename, kernel_cmdline, + initrd_filename, ARM_CPUID_ARM1026); +} + +QEMUMachine integratorcp926_machine = { + "integratorcp926", + "ARM Integrator/CP (ARM926EJ-S)", + integratorcp926_init, +}; + +QEMUMachine integratorcp1026_machine = { + "integratorcp1026", + "ARM Integrator/CP (ARM1026EJ-S)", + integratorcp1026_init, +}; diff --git a/tools/ioemu/hw/iommu.c b/tools/ioemu/hw/iommu.c new file mode 100644 index 0000000000..e7d96c81cc --- /dev/null +++ b/tools/ioemu/hw/iommu.c @@ -0,0 +1,258 @@ +/* + * QEMU SPARC iommu emulation + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* debug iommu */ +//#define DEBUG_IOMMU + +#ifdef DEBUG_IOMMU +#define DPRINTF(fmt, args...) \ +do { printf("IOMMU: " fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) +#endif + +#define IOMMU_NREGS (3*4096/4) +#define IOMMU_CTRL (0x0000 >> 2) +#define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */ +#define IOMMU_CTRL_VERS 0x0f000000 /* Version */ +#define IOMMU_VERSION 0x04000000 +#define IOMMU_CTRL_RNGE 0x0000001c /* Mapping RANGE */ +#define IOMMU_RNGE_16MB 0x00000000 /* 0xff000000 -> 0xffffffff */ +#define IOMMU_RNGE_32MB 0x00000004 /* 0xfe000000 -> 0xffffffff */ +#define IOMMU_RNGE_64MB 0x00000008 /* 0xfc000000 -> 0xffffffff */ +#define IOMMU_RNGE_128MB 0x0000000c /* 0xf8000000 -> 0xffffffff */ +#define IOMMU_RNGE_256MB 0x00000010 /* 0xf0000000 -> 0xffffffff */ +#define IOMMU_RNGE_512MB 0x00000014 /* 0xe0000000 -> 0xffffffff */ +#define IOMMU_RNGE_1GB 0x00000018 /* 0xc0000000 -> 0xffffffff */ +#define IOMMU_RNGE_2GB 0x0000001c /* 0x80000000 -> 0xffffffff */ +#define IOMMU_CTRL_ENAB 0x00000001 /* IOMMU Enable */ +#define IOMMU_CTRL_MASK 0x0000001d + +#define IOMMU_BASE (0x0004 >> 2) +#define IOMMU_BASE_MASK 0x07fffc00 + +#define IOMMU_TLBFLUSH (0x0014 >> 2) +#define IOMMU_TLBFLUSH_MASK 0xffffffff + +#define IOMMU_PGFLUSH (0x0018 >> 2) +#define IOMMU_PGFLUSH_MASK 0xffffffff + +#define IOMMU_SBCFG0 (0x1010 >> 2) /* SBUS configration per-slot */ +#define IOMMU_SBCFG1 (0x1014 >> 2) /* SBUS configration per-slot */ +#define IOMMU_SBCFG2 (0x1018 >> 2) /* SBUS configration per-slot */ +#define IOMMU_SBCFG3 (0x101c >> 2) /* SBUS configration per-slot */ +#define IOMMU_SBCFG_SAB30 0x00010000 /* Phys-address bit 30 when bypass enabled */ +#define IOMMU_SBCFG_BA16 0x00000004 /* Slave supports 16 byte bursts */ +#define IOMMU_SBCFG_BA8 0x00000002 /* Slave supports 8 byte bursts */ +#define IOMMU_SBCFG_BYPASS 0x00000001 /* Bypass IOMMU, treat all addresses + produced by this device as pure + physical. */ +#define IOMMU_SBCFG_MASK 0x00010003 + +#define IOMMU_ARBEN (0x2000 >> 2) /* SBUS arbitration enable */ +#define IOMMU_ARBEN_MASK 0x001f0000 +#define IOMMU_MID 0x00000008 + +/* The format of an iopte in the page tables */ +#define IOPTE_PAGE 0x07ffff00 /* Physical page number (PA[30:12]) */ +#define IOPTE_CACHE 0x00000080 /* Cached (in vme IOCACHE or Viking/MXCC) */ +#define IOPTE_WRITE 0x00000004 /* Writeable */ +#define IOPTE_VALID 0x00000002 /* IOPTE is valid */ +#define IOPTE_WAZ 0x00000001 /* Write as zeros */ + +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define PAGE_MASK (PAGE_SIZE - 1) + +typedef struct IOMMUState { + uint32_t addr; + uint32_t regs[IOMMU_NREGS]; + uint32_t iostart; +} IOMMUState; + +static uint32_t iommu_mem_readw(void *opaque, target_phys_addr_t addr) +{ + IOMMUState *s = opaque; + uint32_t saddr; + + saddr = (addr - s->addr) >> 2; + switch (saddr) { + default: + DPRINTF("read reg[%d] = %x\n", saddr, s->regs[saddr]); + return s->regs[saddr]; + break; + } + return 0; +} + +static void iommu_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + IOMMUState *s = opaque; + uint32_t saddr; + + saddr = (addr - s->addr) >> 2; + DPRINTF("write reg[%d] = %x\n", saddr, val); + switch (saddr) { + case IOMMU_CTRL: + switch (val & IOMMU_CTRL_RNGE) { + case IOMMU_RNGE_16MB: + s->iostart = 0xff000000; + break; + case IOMMU_RNGE_32MB: + s->iostart = 0xfe000000; + break; + case IOMMU_RNGE_64MB: + s->iostart = 0xfc000000; + break; + case IOMMU_RNGE_128MB: + s->iostart = 0xf8000000; + break; + case IOMMU_RNGE_256MB: + s->iostart = 0xf0000000; + break; + case IOMMU_RNGE_512MB: + s->iostart = 0xe0000000; + break; + case IOMMU_RNGE_1GB: + s->iostart = 0xc0000000; + break; + default: + case IOMMU_RNGE_2GB: + s->iostart = 0x80000000; + break; + } + DPRINTF("iostart = %x\n", s->iostart); + s->regs[saddr] = ((val & IOMMU_CTRL_MASK) | IOMMU_VERSION); + break; + case IOMMU_BASE: + s->regs[saddr] = val & IOMMU_BASE_MASK; + break; + case IOMMU_TLBFLUSH: + DPRINTF("tlb flush %x\n", val); + s->regs[saddr] = val & IOMMU_TLBFLUSH_MASK; + break; + case IOMMU_PGFLUSH: + DPRINTF("page flush %x\n", val); + s->regs[saddr] = val & IOMMU_PGFLUSH_MASK; + break; + case IOMMU_SBCFG0: + case IOMMU_SBCFG1: + case IOMMU_SBCFG2: + case IOMMU_SBCFG3: + s->regs[saddr] = val & IOMMU_SBCFG_MASK; + break; + case IOMMU_ARBEN: + // XXX implement SBus probing: fault when reading unmapped + // addresses, fault cause and address stored to MMU/IOMMU + s->regs[saddr] = (val & IOMMU_ARBEN_MASK) | IOMMU_MID; + break; + default: + s->regs[saddr] = val; + break; + } +} + +static CPUReadMemoryFunc *iommu_mem_read[3] = { + iommu_mem_readw, + iommu_mem_readw, + iommu_mem_readw, +}; + +static CPUWriteMemoryFunc *iommu_mem_write[3] = { + iommu_mem_writew, + iommu_mem_writew, + iommu_mem_writew, +}; + +uint32_t iommu_translate_local(void *opaque, uint32_t addr) +{ + IOMMUState *s = opaque; + uint32_t iopte, pa, tmppte; + + iopte = s->regs[1] << 4; + addr &= ~s->iostart; + iopte += (addr >> (PAGE_SHIFT - 2)) & ~3; + pa = ldl_phys(iopte); + tmppte = pa; + pa = ((pa & IOPTE_PAGE) << 4) + (addr & PAGE_MASK); + DPRINTF("xlate dva %x => pa %x (iopte[%x] = %x)\n", addr, pa, iopte, tmppte); + return pa; +} + +static void iommu_save(QEMUFile *f, void *opaque) +{ + IOMMUState *s = opaque; + int i; + + qemu_put_be32s(f, &s->addr); + for (i = 0; i < IOMMU_NREGS; i++) + qemu_put_be32s(f, &s->regs[i]); + qemu_put_be32s(f, &s->iostart); +} + +static int iommu_load(QEMUFile *f, void *opaque, int version_id) +{ + IOMMUState *s = opaque; + int i; + + if (version_id != 1) + return -EINVAL; + + qemu_get_be32s(f, &s->addr); + for (i = 0; i < IOMMU_NREGS; i++) + qemu_put_be32s(f, &s->regs[i]); + qemu_get_be32s(f, &s->iostart); + + return 0; +} + +static void iommu_reset(void *opaque) +{ + IOMMUState *s = opaque; + + memset(s->regs, 0, IOMMU_NREGS * 4); + s->iostart = 0; + s->regs[0] = IOMMU_VERSION; +} + +void *iommu_init(uint32_t addr) +{ + IOMMUState *s; + int iommu_io_memory; + + s = qemu_mallocz(sizeof(IOMMUState)); + if (!s) + return NULL; + + s->addr = addr; + + iommu_io_memory = cpu_register_io_memory(0, iommu_mem_read, iommu_mem_write, s); + cpu_register_physical_memory(addr, IOMMU_NREGS * 4, iommu_io_memory); + + register_savevm("iommu", addr, 1, iommu_save, iommu_load, s); + qemu_register_reset(iommu_reset, s); + return s; +} + diff --git a/tools/ioemu/hw/lance.c b/tools/ioemu/hw/lance.c new file mode 100644 index 0000000000..d1679375d0 --- /dev/null +++ b/tools/ioemu/hw/lance.c @@ -0,0 +1,462 @@ +/* + * QEMU Lance emulation + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* debug LANCE card */ +//#define DEBUG_LANCE + +#ifdef DEBUG_LANCE +#define DPRINTF(fmt, args...) \ +do { printf("LANCE: " fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) +#endif + +#ifndef LANCE_LOG_TX_BUFFERS +#define LANCE_LOG_TX_BUFFERS 4 +#define LANCE_LOG_RX_BUFFERS 4 +#endif + +#define LE_CSR0 0 +#define LE_CSR1 1 +#define LE_CSR2 2 +#define LE_CSR3 3 +#define LE_NREGS (LE_CSR3 + 1) +#define LE_MAXREG LE_CSR3 + +#define LE_RDP 0 +#define LE_RAP 1 + +#define LE_MO_PROM 0x8000 /* Enable promiscuous mode */ + +#define LE_C0_ERR 0x8000 /* Error: set if BAB, SQE, MISS or ME is set */ +#define LE_C0_BABL 0x4000 /* BAB: Babble: tx timeout. */ +#define LE_C0_CERR 0x2000 /* SQE: Signal quality error */ +#define LE_C0_MISS 0x1000 /* MISS: Missed a packet */ +#define LE_C0_MERR 0x0800 /* ME: Memory error */ +#define LE_C0_RINT 0x0400 /* Received interrupt */ +#define LE_C0_TINT 0x0200 /* Transmitter Interrupt */ +#define LE_C0_IDON 0x0100 /* IFIN: Init finished. */ +#define LE_C0_INTR 0x0080 /* Interrupt or error */ +#define LE_C0_INEA 0x0040 /* Interrupt enable */ +#define LE_C0_RXON 0x0020 /* Receiver on */ +#define LE_C0_TXON 0x0010 /* Transmitter on */ +#define LE_C0_TDMD 0x0008 /* Transmitter demand */ +#define LE_C0_STOP 0x0004 /* Stop the card */ +#define LE_C0_STRT 0x0002 /* Start the card */ +#define LE_C0_INIT 0x0001 /* Init the card */ + +#define LE_C3_BSWP 0x4 /* SWAP */ +#define LE_C3_ACON 0x2 /* ALE Control */ +#define LE_C3_BCON 0x1 /* Byte control */ + +/* Receive message descriptor 1 */ +#define LE_R1_OWN 0x80 /* Who owns the entry */ +#define LE_R1_ERR 0x40 /* Error: if FRA, OFL, CRC or BUF is set */ +#define LE_R1_FRA 0x20 /* FRA: Frame error */ +#define LE_R1_OFL 0x10 /* OFL: Frame overflow */ +#define LE_R1_CRC 0x08 /* CRC error */ +#define LE_R1_BUF 0x04 /* BUF: Buffer error */ +#define LE_R1_SOP 0x02 /* Start of packet */ +#define LE_R1_EOP 0x01 /* End of packet */ +#define LE_R1_POK 0x03 /* Packet is complete: SOP + EOP */ + +#define LE_T1_OWN 0x80 /* Lance owns the packet */ +#define LE_T1_ERR 0x40 /* Error summary */ +#define LE_T1_EMORE 0x10 /* Error: more than one retry needed */ +#define LE_T1_EONE 0x08 /* Error: one retry needed */ +#define LE_T1_EDEF 0x04 /* Error: deferred */ +#define LE_T1_SOP 0x02 /* Start of packet */ +#define LE_T1_EOP 0x01 /* End of packet */ +#define LE_T1_POK 0x03 /* Packet is complete: SOP + EOP */ + +#define LE_T3_BUF 0x8000 /* Buffer error */ +#define LE_T3_UFL 0x4000 /* Error underflow */ +#define LE_T3_LCOL 0x1000 /* Error late collision */ +#define LE_T3_CLOS 0x0800 /* Error carrier loss */ +#define LE_T3_RTY 0x0400 /* Error retry */ +#define LE_T3_TDR 0x03ff /* Time Domain Reflectometry counter */ + +#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS)) +#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) +#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29) + +#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS)) +#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) +#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29) + +#define PKT_BUF_SZ 1544 +#define RX_BUFF_SIZE PKT_BUF_SZ +#define TX_BUFF_SIZE PKT_BUF_SZ + +struct lance_rx_desc { + unsigned short rmd0; /* low address of packet */ + unsigned char rmd1_bits; /* descriptor bits */ + unsigned char rmd1_hadr; /* high address of packet */ + short length; /* This length is 2s complement (negative)! + * Buffer length + */ + unsigned short mblength; /* This is the actual number of bytes received */ +}; + +struct lance_tx_desc { + unsigned short tmd0; /* low address of packet */ + unsigned char tmd1_bits; /* descriptor bits */ + unsigned char tmd1_hadr; /* high address of packet */ + short length; /* Length is 2s complement (negative)! */ + unsigned short misc; +}; + +/* The LANCE initialization block, described in databook. */ +/* On the Sparc, this block should be on a DMA region */ +struct lance_init_block { + unsigned short mode; /* Pre-set mode (reg. 15) */ + unsigned char phys_addr[6]; /* Physical ethernet address */ + unsigned filter[2]; /* Multicast filter. */ + + /* Receive and transmit ring base, along with extra bits. */ + unsigned short rx_ptr; /* receive descriptor addr */ + unsigned short rx_len; /* receive len and high addr */ + unsigned short tx_ptr; /* transmit descriptor addr */ + unsigned short tx_len; /* transmit len and high addr */ + + /* The Tx and Rx ring entries must aligned on 8-byte boundaries. */ + struct lance_rx_desc brx_ring[RX_RING_SIZE]; + struct lance_tx_desc btx_ring[TX_RING_SIZE]; + + char tx_buf [TX_RING_SIZE][TX_BUFF_SIZE]; + char pad[2]; /* align rx_buf for copy_and_sum(). */ + char rx_buf [RX_RING_SIZE][RX_BUFF_SIZE]; +}; + +#define LEDMA_REGS 4 +#define LEDMA_MAXADDR (LEDMA_REGS * 4 - 1) + +typedef struct LANCEState { + VLANClientState *vc; + uint8_t macaddr[6]; /* init mac address */ + uint32_t leptr; + uint16_t addr; + uint16_t regs[LE_NREGS]; + uint8_t phys[6]; /* mac address */ + int irq; + unsigned int rxptr, txptr; + uint32_t ledmaregs[LEDMA_REGS]; +} LANCEState; + +static void lance_send(void *opaque); + +static void lance_reset(void *opaque) +{ + LANCEState *s = opaque; + memcpy(s->phys, s->macaddr, 6); + s->rxptr = 0; + s->txptr = 0; + memset(s->regs, 0, LE_NREGS * 2); + s->regs[LE_CSR0] = LE_C0_STOP; + memset(s->ledmaregs, 0, LEDMA_REGS * 4); +} + +static uint32_t lance_mem_readw(void *opaque, target_phys_addr_t addr) +{ + LANCEState *s = opaque; + uint32_t saddr; + + saddr = addr & LE_MAXREG; + switch (saddr >> 1) { + case LE_RDP: + DPRINTF("read dreg[%d] = %4.4x\n", s->addr, s->regs[s->addr]); + return s->regs[s->addr]; + case LE_RAP: + DPRINTF("read areg = %4.4x\n", s->addr); + return s->addr; + default: + DPRINTF("read unknown(%d)\n", saddr>>1); + break; + } + return 0; +} + +static void lance_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + LANCEState *s = opaque; + uint32_t saddr; + uint16_t reg; + + saddr = addr & LE_MAXREG; + switch (saddr >> 1) { + case LE_RDP: + DPRINTF("write dreg[%d] = %4.4x\n", s->addr, val); + switch(s->addr) { + case LE_CSR0: + if (val & LE_C0_STOP) { + s->regs[LE_CSR0] = LE_C0_STOP; + break; + } + + reg = s->regs[LE_CSR0]; + + // 1 = clear for some bits + reg &= ~(val & 0x7f00); + + // generated bits + reg &= ~(LE_C0_ERR | LE_C0_INTR); + if (reg & 0x7100) + reg |= LE_C0_ERR; + if (reg & 0x7f00) + reg |= LE_C0_INTR; + + // direct bit + reg &= ~LE_C0_INEA; + reg |= val & LE_C0_INEA; + + // exclusive bits + if (val & LE_C0_INIT) { + reg |= LE_C0_IDON | LE_C0_INIT; + reg &= ~LE_C0_STOP; + } + else if (val & LE_C0_STRT) { + reg |= LE_C0_STRT | LE_C0_RXON | LE_C0_TXON; + reg &= ~LE_C0_STOP; + } + + s->regs[LE_CSR0] = reg; + break; + case LE_CSR1: + s->leptr = (s->leptr & 0xffff0000) | (val & 0xffff); + s->regs[s->addr] = val; + break; + case LE_CSR2: + s->leptr = (s->leptr & 0xffff) | ((val & 0xffff) << 16); + s->regs[s->addr] = val; + break; + case LE_CSR3: + s->regs[s->addr] = val; + break; + } + break; + case LE_RAP: + DPRINTF("write areg = %4.4x\n", val); + if (val < LE_NREGS) + s->addr = val; + break; + default: + DPRINTF("write unknown(%d) = %4.4x\n", saddr>>1, val); + break; + } + lance_send(s); +} + +static CPUReadMemoryFunc *lance_mem_read[3] = { + lance_mem_readw, + lance_mem_readw, + lance_mem_readw, +}; + +static CPUWriteMemoryFunc *lance_mem_write[3] = { + lance_mem_writew, + lance_mem_writew, + lance_mem_writew, +}; + + +#define MIN_BUF_SIZE 60 + +static int lance_can_receive(void *opaque) +{ + return 1; +} + +static void lance_receive(void *opaque, const uint8_t *buf, int size) +{ + LANCEState *s = opaque; + uint32_t dmaptr = s->leptr + s->ledmaregs[3]; + struct lance_init_block *ib; + unsigned int i, old_rxptr; + uint16_t temp16; + uint8_t temp8; + + DPRINTF("receive size %d\n", size); + if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP) + return; + + ib = (void *) iommu_translate(dmaptr); + + old_rxptr = s->rxptr; + for (i = s->rxptr; i != ((old_rxptr - 1) & RX_RING_MOD_MASK); i = (i + 1) & RX_RING_MOD_MASK) { + cpu_physical_memory_read((uint32_t)&ib->brx_ring[i].rmd1_bits, (void *) &temp8, 1); + if (temp8 == (LE_R1_OWN)) { + s->rxptr = (s->rxptr + 1) & RX_RING_MOD_MASK; + temp16 = size + 4; + bswap16s(&temp16); + cpu_physical_memory_write((uint32_t)&ib->brx_ring[i].mblength, (void *) &temp16, 2); + cpu_physical_memory_write((uint32_t)&ib->rx_buf[i], buf, size); + temp8 = LE_R1_POK; + cpu_physical_memory_write((uint32_t)&ib->brx_ring[i].rmd1_bits, (void *) &temp8, 1); + s->regs[LE_CSR0] |= LE_C0_RINT | LE_C0_INTR; + if (s->regs[LE_CSR0] & LE_C0_INEA) + pic_set_irq(s->irq, 1); + DPRINTF("got packet, len %d\n", size); + return; + } + } +} + +static void lance_send(void *opaque) +{ + LANCEState *s = opaque; + uint32_t dmaptr = s->leptr + s->ledmaregs[3]; + struct lance_init_block *ib; + unsigned int i, old_txptr; + uint16_t temp16; + uint8_t temp8; + char pkt_buf[PKT_BUF_SZ]; + + DPRINTF("sending packet? (csr0 %4.4x)\n", s->regs[LE_CSR0]); + if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP) + return; + + ib = (void *) iommu_translate(dmaptr); + + DPRINTF("sending packet? (dmaptr %8.8x) (ib %p) (btx_ring %p)\n", dmaptr, ib, &ib->btx_ring); + old_txptr = s->txptr; + for (i = s->txptr; i != ((old_txptr - 1) & TX_RING_MOD_MASK); i = (i + 1) & TX_RING_MOD_MASK) { + cpu_physical_memory_read((uint32_t)&ib->btx_ring[i].tmd1_bits, (void *) &temp8, 1); + if (temp8 == (LE_T1_POK|LE_T1_OWN)) { + cpu_physical_memory_read((uint32_t)&ib->btx_ring[i].length, (void *) &temp16, 2); + bswap16s(&temp16); + temp16 = (~temp16) + 1; + cpu_physical_memory_read((uint32_t)&ib->tx_buf[i], pkt_buf, temp16); + DPRINTF("sending packet, len %d\n", temp16); + qemu_send_packet(s->vc, pkt_buf, temp16); + temp8 = LE_T1_POK; + cpu_physical_memory_write((uint32_t)&ib->btx_ring[i].tmd1_bits, (void *) &temp8, 1); + s->txptr = (s->txptr + 1) & TX_RING_MOD_MASK; + s->regs[LE_CSR0] |= LE_C0_TINT | LE_C0_INTR; + } + } + if ((s->regs[LE_CSR0] & LE_C0_INTR) && (s->regs[LE_CSR0] & LE_C0_INEA)) + pic_set_irq(s->irq, 1); +} + +static uint32_t ledma_mem_readl(void *opaque, target_phys_addr_t addr) +{ + LANCEState *s = opaque; + uint32_t saddr; + + saddr = (addr & LEDMA_MAXADDR) >> 2; + return s->ledmaregs[saddr]; +} + +static void ledma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + LANCEState *s = opaque; + uint32_t saddr; + + saddr = (addr & LEDMA_MAXADDR) >> 2; + s->ledmaregs[saddr] = val; +} + +static CPUReadMemoryFunc *ledma_mem_read[3] = { + ledma_mem_readl, + ledma_mem_readl, + ledma_mem_readl, +}; + +static CPUWriteMemoryFunc *ledma_mem_write[3] = { + ledma_mem_writel, + ledma_mem_writel, + ledma_mem_writel, +}; + +static void lance_save(QEMUFile *f, void *opaque) +{ + LANCEState *s = opaque; + int i; + + qemu_put_be32s(f, &s->leptr); + qemu_put_be16s(f, &s->addr); + for (i = 0; i < LE_NREGS; i ++) + qemu_put_be16s(f, &s->regs[i]); + qemu_put_buffer(f, s->phys, 6); + qemu_put_be32s(f, &s->irq); + for (i = 0; i < LEDMA_REGS; i ++) + qemu_put_be32s(f, &s->ledmaregs[i]); +} + +static int lance_load(QEMUFile *f, void *opaque, int version_id) +{ + LANCEState *s = opaque; + int i; + + if (version_id != 1) + return -EINVAL; + + qemu_get_be32s(f, &s->leptr); + qemu_get_be16s(f, &s->addr); + for (i = 0; i < LE_NREGS; i ++) + qemu_get_be16s(f, &s->regs[i]); + qemu_get_buffer(f, s->phys, 6); + qemu_get_be32s(f, &s->irq); + for (i = 0; i < LEDMA_REGS; i ++) + qemu_get_be32s(f, &s->ledmaregs[i]); + return 0; +} + +void lance_init(NICInfo *nd, int irq, uint32_t leaddr, uint32_t ledaddr) +{ + LANCEState *s; + int lance_io_memory, ledma_io_memory; + + s = qemu_mallocz(sizeof(LANCEState)); + if (!s) + return; + + s->irq = irq; + + lance_io_memory = cpu_register_io_memory(0, lance_mem_read, lance_mem_write, s); + cpu_register_physical_memory(leaddr, 4, lance_io_memory); + + ledma_io_memory = cpu_register_io_memory(0, ledma_mem_read, ledma_mem_write, s); + cpu_register_physical_memory(ledaddr, 16, ledma_io_memory); + + memcpy(s->macaddr, nd->macaddr, 6); + + lance_reset(s); + + s->vc = qemu_new_vlan_client(nd->vlan, lance_receive, lance_can_receive, s); + + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "lance macaddr=%02x:%02x:%02x:%02x:%02x:%02x", + s->macaddr[0], + s->macaddr[1], + s->macaddr[2], + s->macaddr[3], + s->macaddr[4], + s->macaddr[5]); + + register_savevm("lance", leaddr, 1, lance_save, lance_load, s); + qemu_register_reset(lance_reset, s); +} + diff --git a/tools/ioemu/hw/m48t59.c b/tools/ioemu/hw/m48t59.c new file mode 100644 index 0000000000..81e64e4418 --- /dev/null +++ b/tools/ioemu/hw/m48t59.c @@ -0,0 +1,608 @@ +/* + * QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms + * + * Copyright (c) 2003-2005 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "m48t59.h" + +//#define DEBUG_NVRAM + +#if defined(DEBUG_NVRAM) +#define NVRAM_PRINTF(fmt, args...) do { printf(fmt , ##args); } while (0) +#else +#define NVRAM_PRINTF(fmt, args...) do { } while (0) +#endif + +/* + * The M48T08 and M48T59 chips are very similar. The newer '59 has + * alarm and a watchdog timer and related control registers. In the + * PPC platform there is also a nvram lock function. + */ +struct m48t59_t { + /* Model parameters */ + int type; // 8 = m48t08, 59 = m48t59 + /* Hardware parameters */ + int IRQ; + int mem_index; + uint32_t mem_base; + uint32_t io_base; + uint16_t size; + /* RTC management */ + time_t time_offset; + time_t stop_time; + /* Alarm & watchdog */ + time_t alarm; + struct QEMUTimer *alrm_timer; + struct QEMUTimer *wd_timer; + /* NVRAM storage */ + uint8_t lock; + uint16_t addr; + uint8_t *buffer; +}; + +/* Fake timer functions */ +/* Generic helpers for BCD */ +static inline uint8_t toBCD (uint8_t value) +{ + return (((value / 10) % 10) << 4) | (value % 10); +} + +static inline uint8_t fromBCD (uint8_t BCD) +{ + return ((BCD >> 4) * 10) + (BCD & 0x0F); +} + +/* RTC management helpers */ +static void get_time (m48t59_t *NVRAM, struct tm *tm) +{ + time_t t; + + t = time(NULL) + NVRAM->time_offset; +#ifdef _WIN32 + memcpy(tm,localtime(&t),sizeof(*tm)); +#else + localtime_r (&t, tm) ; +#endif +} + +static void set_time (m48t59_t *NVRAM, struct tm *tm) +{ + time_t now, new_time; + + new_time = mktime(tm); + now = time(NULL); + NVRAM->time_offset = new_time - now; +} + +/* Alarm management */ +static void alarm_cb (void *opaque) +{ + struct tm tm, tm_now; + uint64_t next_time; + m48t59_t *NVRAM = opaque; + + pic_set_irq(NVRAM->IRQ, 1); + if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 && + (NVRAM->buffer[0x1FF4] & 0x80) == 0 && + (NVRAM->buffer[0x1FF3] & 0x80) == 0 && + (NVRAM->buffer[0x1FF2] & 0x80) == 0) { + /* Repeat once a month */ + get_time(NVRAM, &tm_now); + memcpy(&tm, &tm_now, sizeof(struct tm)); + tm.tm_mon++; + if (tm.tm_mon == 13) { + tm.tm_mon = 1; + tm.tm_year++; + } + next_time = mktime(&tm); + } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && + (NVRAM->buffer[0x1FF4] & 0x80) == 0 && + (NVRAM->buffer[0x1FF3] & 0x80) == 0 && + (NVRAM->buffer[0x1FF2] & 0x80) == 0) { + /* Repeat once a day */ + next_time = 24 * 60 * 60 + mktime(&tm_now); + } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && + (NVRAM->buffer[0x1FF4] & 0x80) != 0 && + (NVRAM->buffer[0x1FF3] & 0x80) == 0 && + (NVRAM->buffer[0x1FF2] & 0x80) == 0) { + /* Repeat once an hour */ + next_time = 60 * 60 + mktime(&tm_now); + } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && + (NVRAM->buffer[0x1FF4] & 0x80) != 0 && + (NVRAM->buffer[0x1FF3] & 0x80) != 0 && + (NVRAM->buffer[0x1FF2] & 0x80) == 0) { + /* Repeat once a minute */ + next_time = 60 + mktime(&tm_now); + } else { + /* Repeat once a second */ + next_time = 1 + mktime(&tm_now); + } + qemu_mod_timer(NVRAM->alrm_timer, next_time * 1000); + pic_set_irq(NVRAM->IRQ, 0); +} + + +static void get_alarm (m48t59_t *NVRAM, struct tm *tm) +{ +#ifdef _WIN32 + memcpy(tm,localtime(&NVRAM->alarm),sizeof(*tm)); +#else + localtime_r (&NVRAM->alarm, tm); +#endif +} + +static void set_alarm (m48t59_t *NVRAM, struct tm *tm) +{ + NVRAM->alarm = mktime(tm); + if (NVRAM->alrm_timer != NULL) { + qemu_del_timer(NVRAM->alrm_timer); + NVRAM->alrm_timer = NULL; + } + if (NVRAM->alarm - time(NULL) > 0) + qemu_mod_timer(NVRAM->alrm_timer, NVRAM->alarm * 1000); +} + +/* Watchdog management */ +static void watchdog_cb (void *opaque) +{ + m48t59_t *NVRAM = opaque; + + NVRAM->buffer[0x1FF0] |= 0x80; + if (NVRAM->buffer[0x1FF7] & 0x80) { + NVRAM->buffer[0x1FF7] = 0x00; + NVRAM->buffer[0x1FFC] &= ~0x40; + /* May it be a hw CPU Reset instead ? */ + qemu_system_reset_request(); + } else { + pic_set_irq(NVRAM->IRQ, 1); + pic_set_irq(NVRAM->IRQ, 0); + } +} + +static void set_up_watchdog (m48t59_t *NVRAM, uint8_t value) +{ + uint64_t interval; /* in 1/16 seconds */ + + if (NVRAM->wd_timer != NULL) { + qemu_del_timer(NVRAM->wd_timer); + NVRAM->wd_timer = NULL; + } + NVRAM->buffer[0x1FF0] &= ~0x80; + if (value != 0) { + interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F); + qemu_mod_timer(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) + + ((interval * 1000) >> 4)); + } +} + +/* Direct access to NVRAM */ +void m48t59_write (m48t59_t *NVRAM, uint32_t addr, uint32_t val) +{ + struct tm tm; + int tmp; + + if (addr > 0x1FF8 && addr < 0x2000) + NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val); + if (NVRAM->type == 8 && + (addr >= 0x1ff0 && addr <= 0x1ff7)) + goto do_write; + switch (addr) { + case 0x1FF0: + /* flags register : read-only */ + break; + case 0x1FF1: + /* unused */ + break; + case 0x1FF2: + /* alarm seconds */ + tmp = fromBCD(val & 0x7F); + if (tmp >= 0 && tmp <= 59) { + get_alarm(NVRAM, &tm); + tm.tm_sec = tmp; + NVRAM->buffer[0x1FF2] = val; + set_alarm(NVRAM, &tm); + } + break; + case 0x1FF3: + /* alarm minutes */ + tmp = fromBCD(val & 0x7F); + if (tmp >= 0 && tmp <= 59) { + get_alarm(NVRAM, &tm); + tm.tm_min = tmp; + NVRAM->buffer[0x1FF3] = val; + set_alarm(NVRAM, &tm); + } + break; + case 0x1FF4: + /* alarm hours */ + tmp = fromBCD(val & 0x3F); + if (tmp >= 0 && tmp <= 23) { + get_alarm(NVRAM, &tm); + tm.tm_hour = tmp; + NVRAM->buffer[0x1FF4] = val; + set_alarm(NVRAM, &tm); + } + break; + case 0x1FF5: + /* alarm date */ + tmp = fromBCD(val & 0x1F); + if (tmp != 0) { + get_alarm(NVRAM, &tm); + tm.tm_mday = tmp; + NVRAM->buffer[0x1FF5] = val; + set_alarm(NVRAM, &tm); + } + break; + case 0x1FF6: + /* interrupts */ + NVRAM->buffer[0x1FF6] = val; + break; + case 0x1FF7: + /* watchdog */ + NVRAM->buffer[0x1FF7] = val; + set_up_watchdog(NVRAM, val); + break; + case 0x1FF8: + /* control */ + NVRAM->buffer[0x1FF8] = (val & ~0xA0) | 0x90; + break; + case 0x1FF9: + /* seconds (BCD) */ + tmp = fromBCD(val & 0x7F); + if (tmp >= 0 && tmp <= 59) { + get_time(NVRAM, &tm); + tm.tm_sec = tmp; + set_time(NVRAM, &tm); + } + if ((val & 0x80) ^ (NVRAM->buffer[0x1FF9] & 0x80)) { + if (val & 0x80) { + NVRAM->stop_time = time(NULL); + } else { + NVRAM->time_offset += NVRAM->stop_time - time(NULL); + NVRAM->stop_time = 0; + } + } + NVRAM->buffer[0x1FF9] = val & 0x80; + break; + case 0x1FFA: + /* minutes (BCD) */ + tmp = fromBCD(val & 0x7F); + if (tmp >= 0 && tmp <= 59) { + get_time(NVRAM, &tm); + tm.tm_min = tmp; + set_time(NVRAM, &tm); + } + break; + case 0x1FFB: + /* hours (BCD) */ + tmp = fromBCD(val & 0x3F); + if (tmp >= 0 && tmp <= 23) { + get_time(NVRAM, &tm); + tm.tm_hour = tmp; + set_time(NVRAM, &tm); + } + break; + case 0x1FFC: + /* day of the week / century */ + tmp = fromBCD(val & 0x07); + get_time(NVRAM, &tm); + tm.tm_wday = tmp; + set_time(NVRAM, &tm); + NVRAM->buffer[0x1FFC] = val & 0x40; + break; + case 0x1FFD: + /* date */ + tmp = fromBCD(val & 0x1F); + if (tmp != 0) { + get_time(NVRAM, &tm); + tm.tm_mday = tmp; + set_time(NVRAM, &tm); + } + break; + case 0x1FFE: + /* month */ + tmp = fromBCD(val & 0x1F); + if (tmp >= 1 && tmp <= 12) { + get_time(NVRAM, &tm); + tm.tm_mon = tmp - 1; + set_time(NVRAM, &tm); + } + break; + case 0x1FFF: + /* year */ + tmp = fromBCD(val); + if (tmp >= 0 && tmp <= 99) { + get_time(NVRAM, &tm); + tm.tm_year = fromBCD(val); + set_time(NVRAM, &tm); + } + break; + default: + /* Check lock registers state */ + if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1)) + break; + if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2)) + break; + do_write: + if (addr < NVRAM->size) { + NVRAM->buffer[addr] = val & 0xFF; + } + break; + } +} + +uint32_t m48t59_read (m48t59_t *NVRAM, uint32_t addr) +{ + struct tm tm; + uint32_t retval = 0xFF; + + if (NVRAM->type == 8 && + (addr >= 0x1ff0 && addr <= 0x1ff7)) + goto do_read; + switch (addr) { + case 0x1FF0: + /* flags register */ + goto do_read; + case 0x1FF1: + /* unused */ + retval = 0; + break; + case 0x1FF2: + /* alarm seconds */ + goto do_read; + case 0x1FF3: + /* alarm minutes */ + goto do_read; + case 0x1FF4: + /* alarm hours */ + goto do_read; + case 0x1FF5: + /* alarm date */ + goto do_read; + case 0x1FF6: + /* interrupts */ + goto do_read; + case 0x1FF7: + /* A read resets the watchdog */ + set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]); + goto do_read; + case 0x1FF8: + /* control */ + goto do_read; + case 0x1FF9: + /* seconds (BCD) */ + get_time(NVRAM, &tm); + retval = (NVRAM->buffer[0x1FF9] & 0x80) | toBCD(tm.tm_sec); + break; + case 0x1FFA: + /* minutes (BCD) */ + get_time(NVRAM, &tm); + retval = toBCD(tm.tm_min); + break; + case 0x1FFB: + /* hours (BCD) */ + get_time(NVRAM, &tm); + retval = toBCD(tm.tm_hour); + break; + case 0x1FFC: + /* day of the week / century */ + get_time(NVRAM, &tm); + retval = NVRAM->buffer[0x1FFC] | tm.tm_wday; + break; + case 0x1FFD: + /* date */ + get_time(NVRAM, &tm); + retval = toBCD(tm.tm_mday); + break; + case 0x1FFE: + /* month */ + get_time(NVRAM, &tm); + retval = toBCD(tm.tm_mon + 1); + break; + case 0x1FFF: + /* year */ + get_time(NVRAM, &tm); + retval = toBCD(tm.tm_year); + break; + default: + /* Check lock registers state */ + if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1)) + break; + if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2)) + break; + do_read: + if (addr < NVRAM->size) { + retval = NVRAM->buffer[addr]; + } + break; + } + if (addr > 0x1FF9 && addr < 0x2000) + NVRAM_PRINTF("0x%08x <= 0x%08x\n", addr, retval); + + return retval; +} + +void m48t59_set_addr (m48t59_t *NVRAM, uint32_t addr) +{ + NVRAM->addr = addr; +} + +void m48t59_toggle_lock (m48t59_t *NVRAM, int lock) +{ + NVRAM->lock ^= 1 << lock; +} + +/* IO access to NVRAM */ +static void NVRAM_writeb (void *opaque, uint32_t addr, uint32_t val) +{ + m48t59_t *NVRAM = opaque; + + addr -= NVRAM->io_base; + NVRAM_PRINTF("0x%08x => 0x%08x\n", addr, val); + switch (addr) { + case 0: + NVRAM->addr &= ~0x00FF; + NVRAM->addr |= val; + break; + case 1: + NVRAM->addr &= ~0xFF00; + NVRAM->addr |= val << 8; + break; + case 3: + m48t59_write(NVRAM, val, NVRAM->addr); + NVRAM->addr = 0x0000; + break; + default: + break; + } +} + +static uint32_t NVRAM_readb (void *opaque, uint32_t addr) +{ + m48t59_t *NVRAM = opaque; + uint32_t retval; + + addr -= NVRAM->io_base; + switch (addr) { + case 3: + retval = m48t59_read(NVRAM, NVRAM->addr); + break; + default: + retval = -1; + break; + } + NVRAM_PRINTF("0x%08x <= 0x%08x\n", addr, retval); + + return retval; +} + +static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + m48t59_t *NVRAM = opaque; + + addr -= NVRAM->mem_base; + m48t59_write(NVRAM, addr, value & 0xff); +} + +static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + m48t59_t *NVRAM = opaque; + + addr -= NVRAM->mem_base; + m48t59_write(NVRAM, addr, (value >> 8) & 0xff); + m48t59_write(NVRAM, addr + 1, value & 0xff); +} + +static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + m48t59_t *NVRAM = opaque; + + addr -= NVRAM->mem_base; + m48t59_write(NVRAM, addr, (value >> 24) & 0xff); + m48t59_write(NVRAM, addr + 1, (value >> 16) & 0xff); + m48t59_write(NVRAM, addr + 2, (value >> 8) & 0xff); + m48t59_write(NVRAM, addr + 3, value & 0xff); +} + +static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr) +{ + m48t59_t *NVRAM = opaque; + uint32_t retval; + + addr -= NVRAM->mem_base; + retval = m48t59_read(NVRAM, addr); + return retval; +} + +static uint32_t nvram_readw (void *opaque, target_phys_addr_t addr) +{ + m48t59_t *NVRAM = opaque; + uint32_t retval; + + addr -= NVRAM->mem_base; + retval = m48t59_read(NVRAM, addr) << 8; + retval |= m48t59_read(NVRAM, addr + 1); + return retval; +} + +static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr) +{ + m48t59_t *NVRAM = opaque; + uint32_t retval; + + addr -= NVRAM->mem_base; + retval = m48t59_read(NVRAM, addr) << 24; + retval |= m48t59_read(NVRAM, addr + 1) << 16; + retval |= m48t59_read(NVRAM, addr + 2) << 8; + retval |= m48t59_read(NVRAM, addr + 3); + return retval; +} + +static CPUWriteMemoryFunc *nvram_write[] = { + &nvram_writeb, + &nvram_writew, + &nvram_writel, +}; + +static CPUReadMemoryFunc *nvram_read[] = { + &nvram_readb, + &nvram_readw, + &nvram_readl, +}; + +/* Initialisation routine */ +m48t59_t *m48t59_init (int IRQ, target_ulong mem_base, + uint32_t io_base, uint16_t size, + int type) +{ + m48t59_t *s; + + s = qemu_mallocz(sizeof(m48t59_t)); + if (!s) + return NULL; + s->buffer = qemu_mallocz(size); + if (!s->buffer) { + qemu_free(s); + return NULL; + } + s->IRQ = IRQ; + s->size = size; + s->mem_base = mem_base; + s->io_base = io_base; + s->addr = 0; + s->type = type; + if (io_base != 0) { + register_ioport_read(io_base, 0x04, 1, NVRAM_readb, s); + register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, s); + } + if (mem_base != 0) { + s->mem_index = cpu_register_io_memory(0, nvram_read, nvram_write, s); + cpu_register_physical_memory(mem_base, 0x4000, s->mem_index); + } + if (type == 59) { + s->alrm_timer = qemu_new_timer(vm_clock, &alarm_cb, s); + s->wd_timer = qemu_new_timer(vm_clock, &watchdog_cb, s); + } + s->lock = 0; + + return s; +} diff --git a/tools/ioemu/hw/m48t59.h b/tools/ioemu/hw/m48t59.h new file mode 100644 index 0000000000..af22dc1123 --- /dev/null +++ b/tools/ioemu/hw/m48t59.h @@ -0,0 +1,13 @@ +#if !defined (__M48T59_H__) +#define __M48T59_H__ + +typedef struct m48t59_t m48t59_t; + +void m48t59_write (m48t59_t *NVRAM, uint32_t addr, uint32_t val); +uint32_t m48t59_read (m48t59_t *NVRAM, uint32_t addr); +void m48t59_toggle_lock (m48t59_t *NVRAM, int lock); +m48t59_t *m48t59_init (int IRQ, target_ulong mem_base, + uint32_t io_base, uint16_t size, + int type); + +#endif /* !defined (__M48T59_H__) */ diff --git a/tools/ioemu/hw/mc146818rtc.c b/tools/ioemu/hw/mc146818rtc.c new file mode 100644 index 0000000000..cf0f2c029e --- /dev/null +++ b/tools/ioemu/hw/mc146818rtc.c @@ -0,0 +1,486 @@ +/* + * QEMU MC146818 RTC emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG_CMOS + +#define RTC_SECONDS 0 +#define RTC_SECONDS_ALARM 1 +#define RTC_MINUTES 2 +#define RTC_MINUTES_ALARM 3 +#define RTC_HOURS 4 +#define RTC_HOURS_ALARM 5 +#define RTC_ALARM_DONT_CARE 0xC0 + +#define RTC_DAY_OF_WEEK 6 +#define RTC_DAY_OF_MONTH 7 +#define RTC_MONTH 8 +#define RTC_YEAR 9 + +#define RTC_REG_A 10 +#define RTC_REG_B 11 +#define RTC_REG_C 12 +#define RTC_REG_D 13 + +#define REG_A_UIP 0x80 + +#define REG_B_SET 0x80 +#define REG_B_PIE 0x40 +#define REG_B_AIE 0x20 +#define REG_B_UIE 0x10 + +struct RTCState { + uint8_t cmos_data[128]; + uint8_t cmos_index; + struct tm current_tm; + int irq; + /* periodic timer */ + QEMUTimer *periodic_timer; + int64_t next_periodic_time; + /* second update */ + int64_t next_second_time; + QEMUTimer *second_timer; + QEMUTimer *second_timer2; +}; + +static void rtc_set_time(RTCState *s); +static void rtc_copy_date(RTCState *s); + +static void rtc_timer_update(RTCState *s, int64_t current_time) +{ + int period_code, period; + int64_t cur_clock, next_irq_clock; + + period_code = s->cmos_data[RTC_REG_A] & 0x0f; + if (period_code != 0 && + (s->cmos_data[RTC_REG_B] & REG_B_PIE)) { + if (period_code <= 2) + period_code += 7; + /* period in 32 Khz cycles */ + period = 1 << (period_code - 1); + /* compute 32 khz clock */ + cur_clock = muldiv64(current_time, 32768, ticks_per_sec); + next_irq_clock = (cur_clock & ~(period - 1)) + period; + s->next_periodic_time = muldiv64(next_irq_clock, ticks_per_sec, 32768) + 1; + qemu_mod_timer(s->periodic_timer, s->next_periodic_time); + } else { + qemu_del_timer(s->periodic_timer); + } +} + +static void rtc_periodic_timer(void *opaque) +{ + RTCState *s = opaque; + + rtc_timer_update(s, s->next_periodic_time); + s->cmos_data[RTC_REG_C] |= 0xc0; + pic_set_irq(s->irq, 1); +} + +static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) +{ + RTCState *s = opaque; + + if ((addr & 1) == 0) { + s->cmos_index = data & 0x7f; + } else { +#ifdef DEBUG_CMOS + printf("cmos: write index=0x%02x val=0x%02x\n", + s->cmos_index, data); +#endif + switch(s->cmos_index) { + case RTC_SECONDS_ALARM: + case RTC_MINUTES_ALARM: + case RTC_HOURS_ALARM: + /* XXX: not supported */ + s->cmos_data[s->cmos_index] = data; + break; + case RTC_SECONDS: + case RTC_MINUTES: + case RTC_HOURS: + case RTC_DAY_OF_WEEK: + case RTC_DAY_OF_MONTH: + case RTC_MONTH: + case RTC_YEAR: + s->cmos_data[s->cmos_index] = data; + /* if in set mode, do not update the time */ + if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { + rtc_set_time(s); + } + break; + case RTC_REG_A: + /* UIP bit is read only */ + s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) | + (s->cmos_data[RTC_REG_A] & REG_A_UIP); + rtc_timer_update(s, qemu_get_clock(vm_clock)); + break; + case RTC_REG_B: + if (data & REG_B_SET) { + /* set mode: reset UIP mode */ + s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; + data &= ~REG_B_UIE; + } else { + /* if disabling set mode, update the time */ + if (s->cmos_data[RTC_REG_B] & REG_B_SET) { + rtc_set_time(s); + } + } + s->cmos_data[RTC_REG_B] = data; + rtc_timer_update(s, qemu_get_clock(vm_clock)); + break; + case RTC_REG_C: + case RTC_REG_D: + /* cannot write to them */ + break; + default: + s->cmos_data[s->cmos_index] = data; + break; + } + } +} + +static inline int to_bcd(RTCState *s, int a) +{ + if (s->cmos_data[RTC_REG_B] & 0x04) { + return a; + } else { + return ((a / 10) << 4) | (a % 10); + } +} + +static inline int from_bcd(RTCState *s, int a) +{ + if (s->cmos_data[RTC_REG_B] & 0x04) { + return a; + } else { + return ((a >> 4) * 10) + (a & 0x0f); + } +} + +static void send_timeoffset_msg(time_t delta) +{ + +/* This routine is used to inform another entity that the + base time offset has changed. For instance, if you + were using xenstore, you might want to write to the store + at this point. Or, you might use some other method. + Whatever you might choose, here's a hook point to implement it. + + One item of note is that this delta is in addition to + any existing offset you might be already using. */ + + return; +} + +static void rtc_set_time(RTCState *s) +{ + struct tm *tm = &s->current_tm; + time_t before, after; + + before = mktime(tm); + tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]); + tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]); + tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f); + if (!(s->cmos_data[RTC_REG_B] & 0x02) && + (s->cmos_data[RTC_HOURS] & 0x80)) { + tm->tm_hour += 12; + } + tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]); + tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]); + tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1; + tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100; + + /* Compute, and send, the additional time delta + We could compute the total time delta, but this is + sufficient, and simple. */ + after = mktime(tm); + send_timeoffset_msg(after-before); +} + +static void rtc_copy_date(RTCState *s) +{ + const struct tm *tm = &s->current_tm; + + s->cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec); + s->cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min); + if (s->cmos_data[RTC_REG_B] & 0x02) { + /* 24 hour format */ + s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour); + } else { + /* 12 hour format */ + s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12); + if (tm->tm_hour >= 12) + s->cmos_data[RTC_HOURS] |= 0x80; + } + s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday); + s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday); + s->cmos_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1); + s->cmos_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100); +} + +/* month is between 0 and 11. */ +static int get_days_in_month(int month, int year) +{ + static const int days_tab[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + int d; + if ((unsigned )month >= 12) + return 31; + d = days_tab[month]; + if (month == 1) { + if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) + d++; + } + return d; +} + +/* update 'tm' to the next second */ +static void rtc_next_second(struct tm *tm) +{ + int days_in_month; + + tm->tm_sec++; + if ((unsigned)tm->tm_sec >= 60) { + tm->tm_sec = 0; + tm->tm_min++; + if ((unsigned)tm->tm_min >= 60) { + tm->tm_min = 0; + tm->tm_hour++; + if ((unsigned)tm->tm_hour >= 24) { + tm->tm_hour = 0; + /* next day */ + tm->tm_wday++; + if ((unsigned)tm->tm_wday >= 7) + tm->tm_wday = 0; + days_in_month = get_days_in_month(tm->tm_mon, + tm->tm_year + 1900); + tm->tm_mday++; + if (tm->tm_mday < 1) { + tm->tm_mday = 1; + } else if (tm->tm_mday > days_in_month) { + tm->tm_mday = 1; + tm->tm_mon++; + if (tm->tm_mon >= 12) { + tm->tm_mon = 0; + tm->tm_year++; + } + } + } + } + } +} + + +static void rtc_update_second(void *opaque) +{ + RTCState *s = opaque; + int64_t delay; + + /* if the oscillator is not in normal operation, we do not update */ + if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) { + s->next_second_time += ticks_per_sec; + qemu_mod_timer(s->second_timer, s->next_second_time); + } else { + rtc_next_second(&s->current_tm); + + if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { + /* update in progress bit */ + s->cmos_data[RTC_REG_A] |= REG_A_UIP; + } + /* should be 244 us = 8 / 32768 seconds, but currently the + timers do not have the necessary resolution. */ + delay = (ticks_per_sec * 1) / 100; + if (delay < 1) + delay = 1; + qemu_mod_timer(s->second_timer2, + s->next_second_time + delay); + } +} + +static void rtc_update_second2(void *opaque) +{ + RTCState *s = opaque; + + if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { + rtc_copy_date(s); + } + + /* check alarm */ + if (s->cmos_data[RTC_REG_B] & REG_B_AIE) { + if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 || + s->cmos_data[RTC_SECONDS_ALARM] == s->current_tm.tm_sec) && + ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 || + s->cmos_data[RTC_MINUTES_ALARM] == s->current_tm.tm_mon) && + ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 || + s->cmos_data[RTC_HOURS_ALARM] == s->current_tm.tm_hour)) { + + s->cmos_data[RTC_REG_C] |= 0xa0; + pic_set_irq(s->irq, 1); + } + } + + /* update ended interrupt */ + if (s->cmos_data[RTC_REG_B] & REG_B_UIE) { + s->cmos_data[RTC_REG_C] |= 0x90; + pic_set_irq(s->irq, 1); + } + + /* clear update in progress bit */ + s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; + + s->next_second_time += ticks_per_sec; + qemu_mod_timer(s->second_timer, s->next_second_time); +} + +static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) +{ + RTCState *s = opaque; + int ret; + if ((addr & 1) == 0) { + return 0xff; + } else { + switch(s->cmos_index) { + case RTC_SECONDS: + case RTC_MINUTES: + case RTC_HOURS: + case RTC_DAY_OF_WEEK: + case RTC_DAY_OF_MONTH: + case RTC_MONTH: + case RTC_YEAR: + ret = s->cmos_data[s->cmos_index]; + break; + case RTC_REG_A: + ret = s->cmos_data[s->cmos_index]; + break; + case RTC_REG_C: + ret = s->cmos_data[s->cmos_index]; + pic_set_irq(s->irq, 0); + s->cmos_data[RTC_REG_C] = 0x00; + break; + default: + ret = s->cmos_data[s->cmos_index]; + break; + } +#ifdef DEBUG_CMOS + printf("cmos: read index=0x%02x val=0x%02x\n", + s->cmos_index, ret); +#endif + return ret; + } +} + +void rtc_set_memory(RTCState *s, int addr, int val) +{ + if (addr >= 0 && addr <= 127) + s->cmos_data[addr] = val; +} + +void rtc_set_date(RTCState *s, const struct tm *tm) +{ + s->current_tm = *tm; + rtc_copy_date(s); +} + +static void rtc_save(QEMUFile *f, void *opaque) +{ + RTCState *s = opaque; + + qemu_put_buffer(f, s->cmos_data, 128); + qemu_put_8s(f, &s->cmos_index); + + qemu_put_be32s(f, &s->current_tm.tm_sec); + qemu_put_be32s(f, &s->current_tm.tm_min); + qemu_put_be32s(f, &s->current_tm.tm_hour); + qemu_put_be32s(f, &s->current_tm.tm_wday); + qemu_put_be32s(f, &s->current_tm.tm_mday); + qemu_put_be32s(f, &s->current_tm.tm_mon); + qemu_put_be32s(f, &s->current_tm.tm_year); + + qemu_put_timer(f, s->periodic_timer); + qemu_put_be64s(f, &s->next_periodic_time); + + qemu_put_be64s(f, &s->next_second_time); + qemu_put_timer(f, s->second_timer); + qemu_put_timer(f, s->second_timer2); +} + +static int rtc_load(QEMUFile *f, void *opaque, int version_id) +{ + RTCState *s = opaque; + + if (version_id != 1) + return -EINVAL; + + qemu_get_buffer(f, s->cmos_data, 128); + qemu_get_8s(f, &s->cmos_index); + + qemu_get_be32s(f, &s->current_tm.tm_sec); + qemu_get_be32s(f, &s->current_tm.tm_min); + qemu_get_be32s(f, &s->current_tm.tm_hour); + qemu_get_be32s(f, &s->current_tm.tm_wday); + qemu_get_be32s(f, &s->current_tm.tm_mday); + qemu_get_be32s(f, &s->current_tm.tm_mon); + qemu_get_be32s(f, &s->current_tm.tm_year); + + qemu_get_timer(f, s->periodic_timer); + qemu_get_be64s(f, &s->next_periodic_time); + + qemu_get_be64s(f, &s->next_second_time); + qemu_get_timer(f, s->second_timer); + qemu_get_timer(f, s->second_timer2); + return 0; +} + +RTCState *rtc_init(int base, int irq) +{ + RTCState *s; + + s = qemu_mallocz(sizeof(RTCState)); + if (!s) + return NULL; + + s->irq = irq; + s->cmos_data[RTC_REG_A] = 0x26; + s->cmos_data[RTC_REG_B] = 0x02; + s->cmos_data[RTC_REG_C] = 0x00; + s->cmos_data[RTC_REG_D] = 0x80; + + s->periodic_timer = qemu_new_timer(vm_clock, + rtc_periodic_timer, s); + s->second_timer = qemu_new_timer(vm_clock, + rtc_update_second, s); + s->second_timer2 = qemu_new_timer(vm_clock, + rtc_update_second2, s); + + s->next_second_time = qemu_get_clock(vm_clock) + (ticks_per_sec * 99) / 100; + qemu_mod_timer(s->second_timer2, s->next_second_time); + + register_ioport_write(base, 2, 1, cmos_ioport_write, s); + register_ioport_read(base, 2, 1, cmos_ioport_read, s); + + register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s); + return s; +} + diff --git a/tools/ioemu/hw/mips_r4k.c b/tools/ioemu/hw/mips_r4k.c new file mode 100644 index 0000000000..0cb11683ae --- /dev/null +++ b/tools/ioemu/hw/mips_r4k.c @@ -0,0 +1,295 @@ +#include "vl.h" + +#define BIOS_FILENAME "mips_bios.bin" +//#define BIOS_FILENAME "system.bin" +#define KERNEL_LOAD_ADDR 0x80010000 +#define INITRD_LOAD_ADDR 0x80800000 + +#define VIRT_TO_PHYS_ADDEND (-0x80000000LL) + +extern FILE *logfile; + +static PITState *pit; + +static void pic_irq_request(void *opaque, int level) +{ + CPUState *env = first_cpu; + if (level) { + env->CP0_Cause |= 0x00000400; + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } else { + env->CP0_Cause &= ~0x00000400; + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } +} + +void cpu_mips_irqctrl_init (void) +{ +} + +/* XXX: do not use a global */ +uint32_t cpu_mips_get_random (CPUState *env) +{ + static uint32_t seed = 0; + uint32_t idx; + seed = seed * 314159 + 1; + idx = (seed >> 16) % (MIPS_TLB_NB - env->CP0_Wired) + env->CP0_Wired; + return idx; +} + +/* MIPS R4K timer */ +uint32_t cpu_mips_get_count (CPUState *env) +{ + return env->CP0_Count + + (uint32_t)muldiv64(qemu_get_clock(vm_clock), + 100 * 1000 * 1000, ticks_per_sec); +} + +static void cpu_mips_update_count (CPUState *env, uint32_t count, + uint32_t compare) +{ + uint64_t now, next; + uint32_t tmp; + + tmp = count; + if (count == compare) + tmp++; + now = qemu_get_clock(vm_clock); + next = now + muldiv64(compare - tmp, ticks_per_sec, 100 * 1000 * 1000); + if (next == now) + next++; +#if 0 + if (logfile) { + fprintf(logfile, "%s: 0x%08llx %08x %08x => 0x%08llx\n", + __func__, now, count, compare, next - now); + } +#endif + /* Store new count and compare registers */ + env->CP0_Compare = compare; + env->CP0_Count = + count - (uint32_t)muldiv64(now, 100 * 1000 * 1000, ticks_per_sec); + /* Adjust timer */ + qemu_mod_timer(env->timer, next); +} + +void cpu_mips_store_count (CPUState *env, uint32_t value) +{ + cpu_mips_update_count(env, value, env->CP0_Compare); +} + +void cpu_mips_store_compare (CPUState *env, uint32_t value) +{ + cpu_mips_update_count(env, cpu_mips_get_count(env), value); + env->CP0_Cause &= ~0x00008000; + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); +} + +static void mips_timer_cb (void *opaque) +{ + CPUState *env; + + env = opaque; +#if 0 + if (logfile) { + fprintf(logfile, "%s\n", __func__); + } +#endif + cpu_mips_update_count(env, cpu_mips_get_count(env), env->CP0_Compare); + env->CP0_Cause |= 0x00008000; + cpu_interrupt(env, CPU_INTERRUPT_HARD); +} + +void cpu_mips_clock_init (CPUState *env) +{ + env->timer = qemu_new_timer(vm_clock, &mips_timer_cb, env); + env->CP0_Compare = 0; + cpu_mips_update_count(env, 1, 0); +} + + +static void io_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) +{ +#if 0 + if (logfile) + fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value); +#endif + cpu_outb(NULL, addr & 0xffff, value); +} + +static uint32_t io_readb (void *opaque, target_phys_addr_t addr) +{ + uint32_t ret = cpu_inb(NULL, addr & 0xffff); +#if 0 + if (logfile) + fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret); +#endif + return ret; +} + +static void io_writew (void *opaque, target_phys_addr_t addr, uint32_t value) +{ +#if 0 + if (logfile) + fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value); +#endif +#ifdef TARGET_WORDS_BIGENDIAN + value = bswap16(value); +#endif + cpu_outw(NULL, addr & 0xffff, value); +} + +static uint32_t io_readw (void *opaque, target_phys_addr_t addr) +{ + uint32_t ret = cpu_inw(NULL, addr & 0xffff); +#ifdef TARGET_WORDS_BIGENDIAN + ret = bswap16(ret); +#endif +#if 0 + if (logfile) + fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret); +#endif + return ret; +} + +static void io_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ +#if 0 + if (logfile) + fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value); +#endif +#ifdef TARGET_WORDS_BIGENDIAN + value = bswap32(value); +#endif + cpu_outl(NULL, addr & 0xffff, value); +} + +static uint32_t io_readl (void *opaque, target_phys_addr_t addr) +{ + uint32_t ret = cpu_inl(NULL, addr & 0xffff); + +#ifdef TARGET_WORDS_BIGENDIAN + ret = bswap32(ret); +#endif +#if 0 + if (logfile) + fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret); +#endif + return ret; +} + +CPUWriteMemoryFunc *io_write[] = { + &io_writeb, + &io_writew, + &io_writel, +}; + +CPUReadMemoryFunc *io_read[] = { + &io_readb, + &io_readw, + &io_readl, +}; + +void mips_r4k_init (int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + char buf[1024]; + int64_t entry = 0; + unsigned long bios_offset; + int io_memory; + int ret; + CPUState *env; + long kernel_size; + + env = cpu_init(); + register_savevm("cpu", 0, 3, cpu_save, cpu_load, env); + + /* allocate RAM */ + cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); + + /* Try to load a BIOS image. If this fails, we continue regardless, + but initialize the hardware ourselves. When a kernel gets + preloaded we also initialize the hardware, since the BIOS wasn't + run. */ + bios_offset = ram_size + vga_ram_size; + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME); + printf("%s: load BIOS '%s' size %d\n", __func__, buf, BIOS_SIZE); + ret = load_image(buf, phys_ram_base + bios_offset); + if (ret == BIOS_SIZE) { + cpu_register_physical_memory((uint32_t)(0x1fc00000), + BIOS_SIZE, bios_offset | IO_MEM_ROM); + env->PC = 0xBFC00000; + if (!kernel_filename) + return; + } else { + /* not fatal */ + fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n", + buf); + } + + kernel_size = 0; + if (kernel_filename) { + kernel_size = load_elf(kernel_filename, VIRT_TO_PHYS_ADDEND, &entry); + if (kernel_size >= 0) + env->PC = entry; + else { + kernel_size = load_image(kernel_filename, + phys_ram_base + KERNEL_LOAD_ADDR + VIRT_TO_PHYS_ADDEND); + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + env->PC = KERNEL_LOAD_ADDR; + } + + /* load initrd */ + if (initrd_filename) { + if (load_image(initrd_filename, + phys_ram_base + INITRD_LOAD_ADDR + VIRT_TO_PHYS_ADDEND) + == (target_ulong) -1) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + } + + /* Store command line. */ + strcpy (phys_ram_base + (16 << 20) - 256, kernel_cmdline); + /* FIXME: little endian support */ + *(int *)(phys_ram_base + (16 << 20) - 260) = tswap32 (0x12345678); + *(int *)(phys_ram_base + (16 << 20) - 264) = tswap32 (ram_size); + } + + /* Init internal devices */ + cpu_mips_clock_init(env); + cpu_mips_irqctrl_init(); + + /* Register 64 KB of ISA IO space at 0x14000000 */ + io_memory = cpu_register_io_memory(0, io_read, io_write, NULL); + cpu_register_physical_memory(0x14000000, 0x00010000, io_memory); + isa_mem_base = 0x10000000; + + isa_pic = pic_init(pic_irq_request, env); + pit = pit_init(0x40, 0); + serial_init(&pic_set_irq_new, isa_pic, 0x3f8, 4, serial_hds[0]); + vga_initialize(NULL, ds, phys_ram_base + ram_size, ram_size, + vga_ram_size, 0, 0); + + if (nd_table[0].vlan) { + if (nd_table[0].model == NULL + || strcmp(nd_table[0].model, "ne2k_isa") == 0) { + isa_ne2000_init(0x300, 9, &nd_table[0]); + } else { + fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model); + exit (1); + } + } +} + +QEMUMachine mips_machine = { + "mips", + "mips r4k platform", + mips_r4k_init, +}; diff --git a/tools/ioemu/hw/ne2000.c b/tools/ioemu/hw/ne2000.c new file mode 100644 index 0000000000..674d83e490 --- /dev/null +++ b/tools/ioemu/hw/ne2000.c @@ -0,0 +1,816 @@ +/* + * QEMU NE2000 emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* debug NE2000 card */ +//#define DEBUG_NE2000 + +#define MAX_ETH_FRAME_SIZE 1514 + +#define E8390_CMD 0x00 /* The command register (for all pages) */ +/* Page 0 register offsets. */ +#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */ +#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */ +#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */ +#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */ +#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */ +#define EN0_TSR 0x04 /* Transmit status reg RD */ +#define EN0_TPSR 0x04 /* Transmit starting page WR */ +#define EN0_NCR 0x05 /* Number of collision reg RD */ +#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */ +#define EN0_FIFO 0x06 /* FIFO RD */ +#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */ +#define EN0_ISR 0x07 /* Interrupt status reg RD WR */ +#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */ +#define EN0_RSARLO 0x08 /* Remote start address reg 0 */ +#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */ +#define EN0_RSARHI 0x09 /* Remote start address reg 1 */ +#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */ +#define EN0_RTL8029ID0 0x0a /* Realtek ID byte #1 RD */ +#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */ +#define EN0_RTL8029ID1 0x0b /* Realtek ID byte #2 RD */ +#define EN0_RSR 0x0c /* rx status reg RD */ +#define EN0_RXCR 0x0c /* RX configuration reg WR */ +#define EN0_TXCR 0x0d /* TX configuration reg WR */ +#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */ +#define EN0_DCFG 0x0e /* Data configuration reg WR */ +#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */ +#define EN0_IMR 0x0f /* Interrupt mask reg WR */ +#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */ + +#define EN1_PHYS 0x11 +#define EN1_CURPAG 0x17 +#define EN1_MULT 0x18 + +#define EN2_STARTPG 0x21 /* Starting page of ring bfr RD */ +#define EN2_STOPPG 0x22 /* Ending page +1 of ring bfr RD */ + +#define EN3_CONFIG0 0x33 +#define EN3_CONFIG1 0x34 +#define EN3_CONFIG2 0x35 +#define EN3_CONFIG3 0x36 + +/* Register accessed at EN_CMD, the 8390 base addr. */ +#define E8390_STOP 0x01 /* Stop and reset the chip */ +#define E8390_START 0x02 /* Start the chip, clear reset */ +#define E8390_TRANS 0x04 /* Transmit a frame */ +#define E8390_RREAD 0x08 /* Remote read */ +#define E8390_RWRITE 0x10 /* Remote write */ +#define E8390_NODMA 0x20 /* Remote DMA */ +#define E8390_PAGE0 0x00 /* Select page chip registers */ +#define E8390_PAGE1 0x40 /* using the two high-order bits */ +#define E8390_PAGE2 0x80 /* Page 3 is invalid. */ + +/* Bits in EN0_ISR - Interrupt status register */ +#define ENISR_RX 0x01 /* Receiver, no error */ +#define ENISR_TX 0x02 /* Transmitter, no error */ +#define ENISR_RX_ERR 0x04 /* Receiver, with error */ +#define ENISR_TX_ERR 0x08 /* Transmitter, with error */ +#define ENISR_OVER 0x10 /* Receiver overwrote the ring */ +#define ENISR_COUNTERS 0x20 /* Counters need emptying */ +#define ENISR_RDC 0x40 /* remote dma complete */ +#define ENISR_RESET 0x80 /* Reset completed */ +#define ENISR_ALL 0x3f /* Interrupts we will enable */ + +/* Bits in received packet status byte and EN0_RSR*/ +#define ENRSR_RXOK 0x01 /* Received a good packet */ +#define ENRSR_CRC 0x02 /* CRC error */ +#define ENRSR_FAE 0x04 /* frame alignment error */ +#define ENRSR_FO 0x08 /* FIFO overrun */ +#define ENRSR_MPA 0x10 /* missed pkt */ +#define ENRSR_PHY 0x20 /* physical/multicast address */ +#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */ +#define ENRSR_DEF 0x80 /* deferring */ + +/* Transmitted packet status, EN0_TSR. */ +#define ENTSR_PTX 0x01 /* Packet transmitted without error */ +#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */ +#define ENTSR_COL 0x04 /* The transmit collided at least once. */ +#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */ +#define ENTSR_CRS 0x10 /* The carrier sense was lost. */ +#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */ +#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */ +#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */ + +#define NE2000_PMEM_SIZE (32*1024) +#define NE2000_PMEM_START (16*1024) +#define NE2000_PMEM_END (NE2000_PMEM_SIZE+NE2000_PMEM_START) +#define NE2000_MEM_SIZE NE2000_PMEM_END + +typedef struct NE2000State { + uint8_t cmd; + uint32_t start; + uint32_t stop; + uint8_t boundary; + uint8_t tsr; + uint8_t tpsr; + uint16_t tcnt; + uint16_t rcnt; + uint32_t rsar; + uint8_t rsr; + uint8_t rxcr; + uint8_t isr; + uint8_t dcfg; + uint8_t imr; + uint8_t phys[6]; /* mac address */ + uint8_t curpag; + uint8_t mult[8]; /* multicast mask array */ + int irq; + PCIDevice *pci_dev; + VLANClientState *vc; + uint8_t macaddr[6]; + uint8_t mem[NE2000_MEM_SIZE]; +} NE2000State; + +static void ne2000_reset(NE2000State *s) +{ + int i; + + s->isr = ENISR_RESET; + memcpy(s->mem, s->macaddr, 6); + s->mem[14] = 0x57; + s->mem[15] = 0x57; + + /* duplicate prom data */ + for(i = 15;i >= 0; i--) { + s->mem[2 * i] = s->mem[i]; + s->mem[2 * i + 1] = s->mem[i]; + } +} + +static void ne2000_update_irq(NE2000State *s) +{ + int isr; + isr = (s->isr & s->imr) & 0x7f; +#if defined(DEBUG_NE2000) + printf("NE2000: Set IRQ line %d to %d (%02x %02x)\n", + s->irq, isr ? 1 : 0, s->isr, s->imr); +#endif + if (s->irq == 16) { + /* PCI irq */ + pci_set_irq(s->pci_dev, 0, (isr != 0)); + } else { + /* ISA irq */ + pic_set_irq(s->irq, (isr != 0)); + } +} + +#define POLYNOMIAL 0x04c11db6 + +/* From FreeBSD */ +/* XXX: optimize */ +static int compute_mcast_idx(const uint8_t *ep) +{ + uint32_t crc; + int carry, i, j; + uint8_t b; + + crc = 0xffffffff; + for (i = 0; i < 6; i++) { + b = *ep++; + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); + crc <<= 1; + b >>= 1; + if (carry) + crc = ((crc ^ POLYNOMIAL) | carry); + } + } + return (crc >> 26); +} + +static int ne2000_buffer_full(NE2000State *s) +{ + int avail, index, boundary; + + index = s->curpag << 8; + boundary = s->boundary << 8; + if (index < boundary) + avail = boundary - index; + else + avail = (s->stop - s->start) - (index - boundary); + if (avail < (MAX_ETH_FRAME_SIZE + 4)) + return 1; + return 0; +} + +static int ne2000_can_receive(void *opaque) +{ + NE2000State *s = opaque; + + if (s->cmd & E8390_STOP) + return 1; + return !ne2000_buffer_full(s); +} + +#define MIN_BUF_SIZE 60 + +static void ne2000_receive(void *opaque, const uint8_t *buf, int size) +{ + NE2000State *s = opaque; + uint8_t *p; + int total_len, next, avail, len, index, mcast_idx; + uint8_t buf1[60]; + static const uint8_t broadcast_macaddr[6] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +#if defined(DEBUG_NE2000) + printf("NE2000: received len=%d\n", size); +#endif + + if (s->cmd & E8390_STOP || ne2000_buffer_full(s)) + return; + + /* XXX: check this */ + if (s->rxcr & 0x10) { + /* promiscuous: receive all */ + } else { + if (!memcmp(buf, broadcast_macaddr, 6)) { + /* broadcast address */ + if (!(s->rxcr & 0x04)) + return; + } else if (buf[0] & 0x01) { + /* multicast */ + if (!(s->rxcr & 0x08)) + return; + mcast_idx = compute_mcast_idx(buf); + if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) + return; + } else if (s->mem[0] == buf[0] && + s->mem[2] == buf[1] && + s->mem[4] == buf[2] && + s->mem[6] == buf[3] && + s->mem[8] == buf[4] && + s->mem[10] == buf[5]) { + /* match */ + } else { + return; + } + } + + + /* if too small buffer, then expand it */ + if (size < MIN_BUF_SIZE) { + memcpy(buf1, buf, size); + memset(buf1 + size, 0, MIN_BUF_SIZE - size); + buf = buf1; + size = MIN_BUF_SIZE; + } + + index = s->curpag << 8; + /* 4 bytes for header */ + total_len = size + 4; + /* address for next packet (4 bytes for CRC) */ + next = index + ((total_len + 4 + 255) & ~0xff); + if (next >= s->stop) + next -= (s->stop - s->start); + /* prepare packet header */ + p = s->mem + index; + s->rsr = ENRSR_RXOK; /* receive status */ + /* XXX: check this */ + if (buf[0] & 0x01) + s->rsr |= ENRSR_PHY; + p[0] = s->rsr; + p[1] = next >> 8; + p[2] = total_len; + p[3] = total_len >> 8; + index += 4; + + /* write packet data */ + while (size > 0) { + avail = s->stop - index; + len = size; + if (len > avail) + len = avail; + memcpy(s->mem + index, buf, len); + buf += len; + index += len; + if (index == s->stop) + index = s->start; + size -= len; + } + s->curpag = next >> 8; + + /* now we can signal we have receive something */ + s->isr |= ENISR_RX; + ne2000_update_irq(s); +} + +static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + NE2000State *s = opaque; + int offset, page, index; + + addr &= 0xf; +#ifdef DEBUG_NE2000 + printf("NE2000: write addr=0x%x val=0x%02x\n", addr, val); +#endif + if (addr == E8390_CMD) { + /* control register */ + s->cmd = val; + if (!(val & E8390_STOP)) { /* START bit makes no sense on RTL8029... */ + s->isr &= ~ENISR_RESET; + /* test specific case: zero length transfert */ + if ((val & (E8390_RREAD | E8390_RWRITE)) && + s->rcnt == 0) { + s->isr |= ENISR_RDC; + ne2000_update_irq(s); + } + if (val & E8390_TRANS) { + index = (s->tpsr << 8); + /* XXX: next 2 lines are a hack to make netware 3.11 work */ + if (index >= NE2000_PMEM_END) + index -= NE2000_PMEM_SIZE; + /* fail safe: check range on the transmitted length */ + if (index + s->tcnt <= NE2000_PMEM_END) { + qemu_send_packet(s->vc, s->mem + index, s->tcnt); + } + /* signal end of transfert */ + s->tsr = ENTSR_PTX; + s->isr |= ENISR_TX; + s->cmd &= ~E8390_TRANS; + ne2000_update_irq(s); + } + } + } else { + page = s->cmd >> 6; + offset = addr | (page << 4); + switch(offset) { + case EN0_STARTPG: + s->start = val << 8; + break; + case EN0_STOPPG: + s->stop = val << 8; + break; + case EN0_BOUNDARY: + s->boundary = val; + break; + case EN0_IMR: + s->imr = val; + ne2000_update_irq(s); + break; + case EN0_TPSR: + s->tpsr = val; + break; + case EN0_TCNTLO: + s->tcnt = (s->tcnt & 0xff00) | val; + break; + case EN0_TCNTHI: + s->tcnt = (s->tcnt & 0x00ff) | (val << 8); + break; + case EN0_RSARLO: + s->rsar = (s->rsar & 0xff00) | val; + break; + case EN0_RSARHI: + s->rsar = (s->rsar & 0x00ff) | (val << 8); + break; + case EN0_RCNTLO: + s->rcnt = (s->rcnt & 0xff00) | val; + break; + case EN0_RCNTHI: + s->rcnt = (s->rcnt & 0x00ff) | (val << 8); + break; + case EN0_RXCR: + s->rxcr = val; + break; + case EN0_DCFG: + s->dcfg = val; + break; + case EN0_ISR: + s->isr &= ~(val & 0x7f); + ne2000_update_irq(s); + break; + case EN1_PHYS ... EN1_PHYS + 5: + s->phys[offset - EN1_PHYS] = val; + break; + case EN1_CURPAG: + s->curpag = val; + break; + case EN1_MULT ... EN1_MULT + 7: + s->mult[offset - EN1_MULT] = val; + break; + } + } +} + +static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr) +{ + NE2000State *s = opaque; + int offset, page, ret; + + addr &= 0xf; + if (addr == E8390_CMD) { + ret = s->cmd; + } else { + page = s->cmd >> 6; + offset = addr | (page << 4); + switch(offset) { + case EN0_TSR: + ret = s->tsr; + break; + case EN0_BOUNDARY: + ret = s->boundary; + break; + case EN0_ISR: + ret = s->isr; + break; + case EN0_RSARLO: + ret = s->rsar & 0x00ff; + break; + case EN0_RSARHI: + ret = s->rsar >> 8; + break; + case EN1_PHYS ... EN1_PHYS + 5: + ret = s->phys[offset - EN1_PHYS]; + break; + case EN1_CURPAG: + ret = s->curpag; + break; + case EN1_MULT ... EN1_MULT + 7: + ret = s->mult[offset - EN1_MULT]; + break; + case EN0_RSR: + ret = s->rsr; + break; + case EN2_STARTPG: + ret = s->start >> 8; + break; + case EN2_STOPPG: + ret = s->stop >> 8; + break; + case EN0_RTL8029ID0: + ret = 0x50; + break; + case EN0_RTL8029ID1: + ret = 0x43; + break; + case EN3_CONFIG0: + ret = 0; /* 10baseT media */ + break; + case EN3_CONFIG2: + ret = 0x40; /* 10baseT active */ + break; + case EN3_CONFIG3: + ret = 0x40; /* Full duplex */ + break; + default: + ret = 0x00; + break; + } + } +#ifdef DEBUG_NE2000 + printf("NE2000: read addr=0x%x val=%02x\n", addr, ret); +#endif + return ret; +} + +static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr, + uint32_t val) +{ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + s->mem[addr] = val; + } +} + +static inline void ne2000_mem_writew(NE2000State *s, uint32_t addr, + uint32_t val) +{ + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + *(uint16_t *)(s->mem + addr) = cpu_to_le16(val); + } +} + +static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr, + uint32_t val) +{ + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + cpu_to_le32wu((uint32_t *)(s->mem + addr), val); + } +} + +static inline uint32_t ne2000_mem_readb(NE2000State *s, uint32_t addr) +{ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + return s->mem[addr]; + } else { + return 0xff; + } +} + +static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr) +{ + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + return le16_to_cpu(*(uint16_t *)(s->mem + addr)); + } else { + return 0xffff; + } +} + +static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr) +{ + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + return le32_to_cpupu((uint32_t *)(s->mem + addr)); + } else { + return 0xffffffff; + } +} + +static inline void ne2000_dma_update(NE2000State *s, int len) +{ + s->rsar += len; + /* wrap */ + /* XXX: check what to do if rsar > stop */ + if (s->rsar == s->stop) + s->rsar = s->start; + + if (s->rcnt <= len) { + s->rcnt = 0; + /* signal end of transfert */ + s->isr |= ENISR_RDC; + ne2000_update_irq(s); + } else { + s->rcnt -= len; + } +} + +static void ne2000_asic_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + NE2000State *s = opaque; + +#ifdef DEBUG_NE2000 + printf("NE2000: asic write val=0x%04x\n", val); +#endif + if (s->rcnt == 0) + return; + if (s->dcfg & 0x01) { + /* 16 bit access */ + ne2000_mem_writew(s, s->rsar, val); + ne2000_dma_update(s, 2); + } else { + /* 8 bit access */ + ne2000_mem_writeb(s, s->rsar, val); + ne2000_dma_update(s, 1); + } +} + +static uint32_t ne2000_asic_ioport_read(void *opaque, uint32_t addr) +{ + NE2000State *s = opaque; + int ret; + + if (s->dcfg & 0x01) { + /* 16 bit access */ + ret = ne2000_mem_readw(s, s->rsar); + ne2000_dma_update(s, 2); + } else { + /* 8 bit access */ + ret = ne2000_mem_readb(s, s->rsar); + ne2000_dma_update(s, 1); + } +#ifdef DEBUG_NE2000 + printf("NE2000: asic read val=0x%04x\n", ret); +#endif + return ret; +} + +static void ne2000_asic_ioport_writel(void *opaque, uint32_t addr, uint32_t val) +{ + NE2000State *s = opaque; + +#ifdef DEBUG_NE2000 + printf("NE2000: asic writel val=0x%04x\n", val); +#endif + if (s->rcnt == 0) + return; + /* 32 bit access */ + ne2000_mem_writel(s, s->rsar, val); + ne2000_dma_update(s, 4); +} + +static uint32_t ne2000_asic_ioport_readl(void *opaque, uint32_t addr) +{ + NE2000State *s = opaque; + int ret; + + /* 32 bit access */ + ret = ne2000_mem_readl(s, s->rsar); + ne2000_dma_update(s, 4); +#ifdef DEBUG_NE2000 + printf("NE2000: asic readl val=0x%04x\n", ret); +#endif + return ret; +} + +static void ne2000_reset_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + /* nothing to do (end of reset pulse) */ +} + +static uint32_t ne2000_reset_ioport_read(void *opaque, uint32_t addr) +{ + NE2000State *s = opaque; + ne2000_reset(s); + return 0; +} + +static void ne2000_save(QEMUFile* f,void* opaque) +{ + NE2000State* s=(NE2000State*)opaque; + + qemu_put_8s(f, &s->rxcr); + + qemu_put_8s(f, &s->cmd); + qemu_put_be32s(f, &s->start); + qemu_put_be32s(f, &s->stop); + qemu_put_8s(f, &s->boundary); + qemu_put_8s(f, &s->tsr); + qemu_put_8s(f, &s->tpsr); + qemu_put_be16s(f, &s->tcnt); + qemu_put_be16s(f, &s->rcnt); + qemu_put_be32s(f, &s->rsar); + qemu_put_8s(f, &s->rsr); + qemu_put_8s(f, &s->isr); + qemu_put_8s(f, &s->dcfg); + qemu_put_8s(f, &s->imr); + qemu_put_buffer(f, s->phys, 6); + qemu_put_8s(f, &s->curpag); + qemu_put_buffer(f, s->mult, 8); + qemu_put_be32s(f, &s->irq); + qemu_put_buffer(f, s->mem, NE2000_MEM_SIZE); +} + +static int ne2000_load(QEMUFile* f,void* opaque,int version_id) +{ + NE2000State* s=(NE2000State*)opaque; + + if (version_id == 2) { + qemu_get_8s(f, &s->rxcr); + } else if (version_id == 1) { + s->rxcr = 0x0c; + } else { + return -EINVAL; + } + + qemu_get_8s(f, &s->cmd); + qemu_get_be32s(f, &s->start); + qemu_get_be32s(f, &s->stop); + qemu_get_8s(f, &s->boundary); + qemu_get_8s(f, &s->tsr); + qemu_get_8s(f, &s->tpsr); + qemu_get_be16s(f, &s->tcnt); + qemu_get_be16s(f, &s->rcnt); + qemu_get_be32s(f, &s->rsar); + qemu_get_8s(f, &s->rsr); + qemu_get_8s(f, &s->isr); + qemu_get_8s(f, &s->dcfg); + qemu_get_8s(f, &s->imr); + qemu_get_buffer(f, s->phys, 6); + qemu_get_8s(f, &s->curpag); + qemu_get_buffer(f, s->mult, 8); + qemu_get_be32s(f, &s->irq); + qemu_get_buffer(f, s->mem, NE2000_MEM_SIZE); + + return 0; +} + +void isa_ne2000_init(int base, int irq, NICInfo *nd) +{ + NE2000State *s; + + s = qemu_mallocz(sizeof(NE2000State)); + if (!s) + return; + + register_ioport_write(base, 16, 1, ne2000_ioport_write, s); + register_ioport_read(base, 16, 1, ne2000_ioport_read, s); + + register_ioport_write(base + 0x10, 1, 1, ne2000_asic_ioport_write, s); + register_ioport_read(base + 0x10, 1, 1, ne2000_asic_ioport_read, s); + register_ioport_write(base + 0x10, 2, 2, ne2000_asic_ioport_write, s); + register_ioport_read(base + 0x10, 2, 2, ne2000_asic_ioport_read, s); + + register_ioport_write(base + 0x1f, 1, 1, ne2000_reset_ioport_write, s); + register_ioport_read(base + 0x1f, 1, 1, ne2000_reset_ioport_read, s); + s->irq = irq; + memcpy(s->macaddr, nd->macaddr, 6); + + ne2000_reset(s); + + s->vc = qemu_new_vlan_client(nd->vlan, ne2000_receive, + ne2000_can_receive, s); + + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "ne2000 macaddr=%02x:%02x:%02x:%02x:%02x:%02x", + s->macaddr[0], + s->macaddr[1], + s->macaddr[2], + s->macaddr[3], + s->macaddr[4], + s->macaddr[5]); + + register_savevm("ne2000", 0, 2, ne2000_save, ne2000_load, s); +} + +/***********************************************************/ +/* PCI NE2000 definitions */ + +typedef struct PCINE2000State { + PCIDevice dev; + NE2000State ne2000; +} PCINE2000State; + +static void ne2000_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + PCINE2000State *d = (PCINE2000State *)pci_dev; + NE2000State *s = &d->ne2000; + + register_ioport_write(addr, 16, 1, ne2000_ioport_write, s); + register_ioport_read(addr, 16, 1, ne2000_ioport_read, s); + + register_ioport_write(addr + 0x10, 1, 1, ne2000_asic_ioport_write, s); + register_ioport_read(addr + 0x10, 1, 1, ne2000_asic_ioport_read, s); + register_ioport_write(addr + 0x10, 2, 2, ne2000_asic_ioport_write, s); + register_ioport_read(addr + 0x10, 2, 2, ne2000_asic_ioport_read, s); + register_ioport_write(addr + 0x10, 4, 4, ne2000_asic_ioport_writel, s); + register_ioport_read(addr + 0x10, 4, 4, ne2000_asic_ioport_readl, s); + + register_ioport_write(addr + 0x1f, 1, 1, ne2000_reset_ioport_write, s); + register_ioport_read(addr + 0x1f, 1, 1, ne2000_reset_ioport_read, s); +} + +void pci_ne2000_init(PCIBus *bus, NICInfo *nd) +{ + PCINE2000State *d; + NE2000State *s; + uint8_t *pci_conf; + + d = (PCINE2000State *)pci_register_device(bus, + "NE2000", sizeof(PCINE2000State), + -1, + NULL, NULL); + pci_conf = d->dev.config; + pci_conf[0x00] = 0xec; // Realtek 8029 + pci_conf[0x01] = 0x10; + pci_conf[0x02] = 0x29; + pci_conf[0x03] = 0x80; + pci_conf[0x0a] = 0x00; // ethernet network controller + pci_conf[0x0b] = 0x02; + pci_conf[0x0e] = 0x00; // header_type + pci_conf[0x3d] = 1; // interrupt pin 0 + + pci_register_io_region(&d->dev, 0, 0x100, + PCI_ADDRESS_SPACE_IO, ne2000_map); + s = &d->ne2000; + s->irq = 16; // PCI interrupt + s->pci_dev = (PCIDevice *)d; + memcpy(s->macaddr, nd->macaddr, 6); + ne2000_reset(s); + s->vc = qemu_new_vlan_client(nd->vlan, ne2000_receive, + ne2000_can_receive, s); + + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "ne2000 pci macaddr=%02x:%02x:%02x:%02x:%02x:%02x", + s->macaddr[0], + s->macaddr[1], + s->macaddr[2], + s->macaddr[3], + s->macaddr[4], + s->macaddr[5]); + + /* XXX: instance number ? */ + register_savevm("ne2000", 0, 2, ne2000_save, ne2000_load, s); + register_savevm("ne2000_pci", 0, 1, generic_pci_save, generic_pci_load, + &d->dev); +} diff --git a/tools/ioemu/hw/openpic.c b/tools/ioemu/hw/openpic.c new file mode 100644 index 0000000000..31773373ac --- /dev/null +++ b/tools/ioemu/hw/openpic.c @@ -0,0 +1,1027 @@ +/* + * OpenPIC emulation + * + * Copyright (c) 2004 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* + * + * Based on OpenPic implementations: + * - Intel GW80314 I/O compagnion chip developper's manual + * - Motorola MPC8245 & MPC8540 user manuals. + * - Motorola MCP750 (aka Raven) programmer manual. + * - Motorola Harrier programmer manuel + * + * Serial interrupts, as implemented in Raven chipset are not supported yet. + * + */ +#include "vl.h" + +//#define DEBUG_OPENPIC + +#ifdef DEBUG_OPENPIC +#define DPRINTF(fmt, args...) do { printf(fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) do { } while (0) +#endif +#define ERROR(fmr, args...) do { printf("ERROR: " fmr , ##args); } while (0) + +#define USE_MPCxxx /* Intel model is broken, for now */ + +#if defined (USE_INTEL_GW80314) +/* Intel GW80314 I/O Companion chip */ + +#define MAX_CPU 4 +#define MAX_IRQ 32 +#define MAX_DBL 4 +#define MAX_MBX 4 +#define MAX_TMR 4 +#define VECTOR_BITS 8 +#define MAX_IPI 0 + +#define VID (0x00000000) + +#define OPENPIC_LITTLE_ENDIAN 1 +#define OPENPIC_BIG_ENDIAN 0 + +#elif defined(USE_MPCxxx) + +#define MAX_CPU 2 +#define MAX_IRQ 64 +#define EXT_IRQ 48 +#define MAX_DBL 0 +#define MAX_MBX 0 +#define MAX_TMR 4 +#define VECTOR_BITS 8 +#define MAX_IPI 4 +#define VID 0x03 /* MPIC version ID */ +#define VENI 0x00000000 /* Vendor ID */ + +enum { + IRQ_IPVP = 0, + IRQ_IDE, +}; + +#define OPENPIC_LITTLE_ENDIAN 1 +#define OPENPIC_BIG_ENDIAN 0 + +#else +#error "Please select which OpenPic implementation is to be emulated" +#endif + +#if (OPENPIC_BIG_ENDIAN && !TARGET_WORDS_BIGENDIAN) || \ + (OPENPIC_LITTLE_ENDIAN && TARGET_WORDS_BIGENDIAN) +#define OPENPIC_SWAP +#endif + +/* Interrupt definitions */ +#define IRQ_FE (EXT_IRQ) /* Internal functional IRQ */ +#define IRQ_ERR (EXT_IRQ + 1) /* Error IRQ */ +#define IRQ_TIM0 (EXT_IRQ + 2) /* First timer IRQ */ +#if MAX_IPI > 0 +#define IRQ_IPI0 (IRQ_TIM0 + MAX_TMR) /* First IPI IRQ */ +#define IRQ_DBL0 (IRQ_IPI0 + (MAX_CPU * MAX_IPI)) /* First doorbell IRQ */ +#else +#define IRQ_DBL0 (IRQ_TIM0 + MAX_TMR) /* First doorbell IRQ */ +#define IRQ_MBX0 (IRQ_DBL0 + MAX_DBL) /* First mailbox IRQ */ +#endif + +#define BF_WIDTH(_bits_) \ +(((_bits_) + (sizeof(uint32_t) * 8) - 1) / (sizeof(uint32_t) * 8)) + +static inline void set_bit (uint32_t *field, int bit) +{ + field[bit >> 5] |= 1 << (bit & 0x1F); +} + +static inline void reset_bit (uint32_t *field, int bit) +{ + field[bit >> 5] &= ~(1 << (bit & 0x1F)); +} + +static inline int test_bit (uint32_t *field, int bit) +{ + return (field[bit >> 5] & 1 << (bit & 0x1F)) != 0; +} + +enum { + IRQ_EXTERNAL = 0x01, + IRQ_INTERNAL = 0x02, + IRQ_TIMER = 0x04, + IRQ_SPECIAL = 0x08, +} IRQ_src_type; + +typedef struct IRQ_queue_t { + uint32_t queue[BF_WIDTH(MAX_IRQ)]; + int next; + int priority; +} IRQ_queue_t; + +typedef struct IRQ_src_t { + uint32_t ipvp; /* IRQ vector/priority register */ + uint32_t ide; /* IRQ destination register */ + int type; + int last_cpu; + int pending; /* TRUE if IRQ is pending */ +} IRQ_src_t; + +enum IPVP_bits { + IPVP_MASK = 31, + IPVP_ACTIVITY = 30, + IPVP_MODE = 29, + IPVP_POLARITY = 23, + IPVP_SENSE = 22, +}; +#define IPVP_PRIORITY_MASK (0x1F << 16) +#define IPVP_PRIORITY(_ipvpr_) ((int)(((_ipvpr_) & IPVP_PRIORITY_MASK) >> 16)) +#define IPVP_VECTOR_MASK ((1 << VECTOR_BITS) - 1) +#define IPVP_VECTOR(_ipvpr_) ((_ipvpr_) & IPVP_VECTOR_MASK) + +typedef struct IRQ_dst_t { + uint32_t pctp; /* CPU current task priority */ + uint32_t pcsr; /* CPU sensitivity register */ + IRQ_queue_t raised; + IRQ_queue_t servicing; + CPUState *env; +} IRQ_dst_t; + +struct openpic_t { + PCIDevice pci_dev; + int mem_index; + /* Global registers */ + uint32_t frep; /* Feature reporting register */ + uint32_t glbc; /* Global configuration register */ + uint32_t micr; /* MPIC interrupt configuration register */ + uint32_t veni; /* Vendor identification register */ + uint32_t spve; /* Spurious vector register */ + uint32_t tifr; /* Timer frequency reporting register */ + /* Source registers */ + IRQ_src_t src[MAX_IRQ]; + /* Local registers per output pin */ + IRQ_dst_t dst[MAX_CPU]; + int nb_cpus; + /* Timer registers */ + struct { + uint32_t ticc; /* Global timer current count register */ + uint32_t tibc; /* Global timer base count register */ + } timers[MAX_TMR]; +#if MAX_DBL > 0 + /* Doorbell registers */ + uint32_t dar; /* Doorbell activate register */ + struct { + uint32_t dmr; /* Doorbell messaging register */ + } doorbells[MAX_DBL]; +#endif +#if MAX_MBX > 0 + /* Mailbox registers */ + struct { + uint32_t mbr; /* Mailbox register */ + } mailboxes[MAX_MAILBOXES]; +#endif +}; + +static inline void IRQ_setbit (IRQ_queue_t *q, int n_IRQ) +{ + set_bit(q->queue, n_IRQ); +} + +static inline void IRQ_resetbit (IRQ_queue_t *q, int n_IRQ) +{ + reset_bit(q->queue, n_IRQ); +} + +static inline int IRQ_testbit (IRQ_queue_t *q, int n_IRQ) +{ + return test_bit(q->queue, n_IRQ); +} + +static void IRQ_check (openpic_t *opp, IRQ_queue_t *q) +{ + int next, i; + int priority; + + next = -1; + priority = -1; + for (i = 0; i < MAX_IRQ; i++) { + if (IRQ_testbit(q, i)) { + DPRINTF("IRQ_check: irq %d set ipvp_pr=%d pr=%d\n", + i, IPVP_PRIORITY(opp->src[i].ipvp), priority); + if (IPVP_PRIORITY(opp->src[i].ipvp) > priority) { + next = i; + priority = IPVP_PRIORITY(opp->src[i].ipvp); + } + } + } + q->next = next; + q->priority = priority; +} + +static int IRQ_get_next (openpic_t *opp, IRQ_queue_t *q) +{ + if (q->next == -1) { + /* XXX: optimize */ + IRQ_check(opp, q); + } + + return q->next; +} + +static void IRQ_local_pipe (openpic_t *opp, int n_CPU, int n_IRQ) +{ + IRQ_dst_t *dst; + IRQ_src_t *src; + int priority; + + dst = &opp->dst[n_CPU]; + src = &opp->src[n_IRQ]; + priority = IPVP_PRIORITY(src->ipvp); + if (priority <= dst->pctp) { + /* Too low priority */ + return; + } + if (IRQ_testbit(&dst->raised, n_IRQ)) { + /* Interrupt miss */ + return; + } + set_bit(&src->ipvp, IPVP_ACTIVITY); + IRQ_setbit(&dst->raised, n_IRQ); + if (priority > dst->raised.priority) { + IRQ_get_next(opp, &dst->raised); + DPRINTF("Raise CPU IRQ\n"); + cpu_interrupt(dst->env, CPU_INTERRUPT_HARD); + } +} + +/* update pic state because registers for n_IRQ have changed value */ +static void openpic_update_irq(openpic_t *opp, int n_IRQ) +{ + IRQ_src_t *src; + int i; + + src = &opp->src[n_IRQ]; + + if (!src->pending) { + /* no irq pending */ + return; + } + if (test_bit(&src->ipvp, IPVP_MASK)) { + /* Interrupt source is disabled */ + return; + } + if (IPVP_PRIORITY(src->ipvp) == 0) { + /* Priority set to zero */ + return; + } + if (test_bit(&src->ipvp, IPVP_ACTIVITY)) { + /* IRQ already active */ + return; + } + if (src->ide == 0x00000000) { + /* No target */ + return; + } + + if (!test_bit(&src->ipvp, IPVP_MODE) || + src->ide == (1 << src->last_cpu)) { + /* Directed delivery mode */ + for (i = 0; i < opp->nb_cpus; i++) { + if (test_bit(&src->ide, i)) + IRQ_local_pipe(opp, i, n_IRQ); + } + } else { + /* Distributed delivery mode */ + /* XXX: incorrect code */ + for (i = src->last_cpu; i < src->last_cpu; i++) { + if (i == MAX_IRQ) + i = 0; + if (test_bit(&src->ide, i)) { + IRQ_local_pipe(opp, i, n_IRQ); + src->last_cpu = i; + break; + } + } + } +} + +void openpic_set_irq(void *opaque, int n_IRQ, int level) +{ + openpic_t *opp = opaque; + IRQ_src_t *src; + + src = &opp->src[n_IRQ]; + DPRINTF("openpic: set irq %d = %d ipvp=%08x\n", + n_IRQ, level, src->ipvp); + if (test_bit(&src->ipvp, IPVP_SENSE)) { + /* level-sensitive irq */ + src->pending = level; + if (!level) + reset_bit(&src->ipvp, IPVP_ACTIVITY); + } else { + /* edge-sensitive irq */ + if (level) + src->pending = 1; + } + openpic_update_irq(opp, n_IRQ); +} + +static void openpic_reset (openpic_t *opp) +{ + int i; + + opp->glbc = 0x80000000; + /* Initialise controller registers */ + opp->frep = ((EXT_IRQ - 1) << 16) | ((MAX_CPU - 1) << 8) | VID; + opp->veni = VENI; + opp->spve = 0x000000FF; + opp->tifr = 0x003F7A00; + /* ? */ + opp->micr = 0x00000000; + /* Initialise IRQ sources */ + for (i = 0; i < MAX_IRQ; i++) { + opp->src[i].ipvp = 0xA0000000; + opp->src[i].ide = 0x00000000; + } + /* Initialise IRQ destinations */ + for (i = 0; i < opp->nb_cpus; i++) { + opp->dst[i].pctp = 0x0000000F; + opp->dst[i].pcsr = 0x00000000; + memset(&opp->dst[i].raised, 0, sizeof(IRQ_queue_t)); + memset(&opp->dst[i].servicing, 0, sizeof(IRQ_queue_t)); + } + /* Initialise timers */ + for (i = 0; i < MAX_TMR; i++) { + opp->timers[i].ticc = 0x00000000; + opp->timers[i].tibc = 0x80000000; + } + /* Initialise doorbells */ +#if MAX_DBL > 0 + opp->dar = 0x00000000; + for (i = 0; i < MAX_DBL; i++) { + opp->doorbells[i].dmr = 0x00000000; + } +#endif + /* Initialise mailboxes */ +#if MAX_MBX > 0 + for (i = 0; i < MAX_MBX; i++) { /* ? */ + opp->mailboxes[i].mbr = 0x00000000; + } +#endif + /* Go out of RESET state */ + opp->glbc = 0x00000000; +} + +static inline uint32_t read_IRQreg (openpic_t *opp, int n_IRQ, uint32_t reg) +{ + uint32_t retval; + + switch (reg) { + case IRQ_IPVP: + retval = opp->src[n_IRQ].ipvp; + break; + case IRQ_IDE: + retval = opp->src[n_IRQ].ide; + break; + } + + return retval; +} + +static inline void write_IRQreg (openpic_t *opp, int n_IRQ, + uint32_t reg, uint32_t val) +{ + uint32_t tmp; + + switch (reg) { + case IRQ_IPVP: + /* NOTE: not fully accurate for special IRQs, but simple and + sufficient */ + /* ACTIVITY bit is read-only */ + opp->src[n_IRQ].ipvp = + (opp->src[n_IRQ].ipvp & 0x40000000) | + (val & 0x800F00FF); + openpic_update_irq(opp, n_IRQ); + DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n", + n_IRQ, val, opp->src[n_IRQ].ipvp); + break; + case IRQ_IDE: + tmp = val & 0xC0000000; + tmp |= val & ((1 << MAX_CPU) - 1); + opp->src[n_IRQ].ide = tmp; + DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide); + break; + } +} + +#if 0 // Code provision for Intel model +#if MAX_DBL > 0 +static uint32_t read_doorbell_register (openpic_t *opp, + int n_dbl, uint32_t offset) +{ + uint32_t retval; + + switch (offset) { + case DBL_IPVP_OFFSET: + retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP); + break; + case DBL_IDE_OFFSET: + retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE); + break; + case DBL_DMR_OFFSET: + retval = opp->doorbells[n_dbl].dmr; + break; + } + + return retval; +} + +static void write_doorbell_register (penpic_t *opp, int n_dbl, + uint32_t offset, uint32_t value) +{ + switch (offset) { + case DBL_IVPR_OFFSET: + write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP, value); + break; + case DBL_IDE_OFFSET: + write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE, value); + break; + case DBL_DMR_OFFSET: + opp->doorbells[n_dbl].dmr = value; + break; + } +} +#endif + +#if MAX_MBX > 0 +static uint32_t read_mailbox_register (openpic_t *opp, + int n_mbx, uint32_t offset) +{ + uint32_t retval; + + switch (offset) { + case MBX_MBR_OFFSET: + retval = opp->mailboxes[n_mbx].mbr; + break; + case MBX_IVPR_OFFSET: + retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP); + break; + case MBX_DMR_OFFSET: + retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE); + break; + } + + return retval; +} + +static void write_mailbox_register (openpic_t *opp, int n_mbx, + uint32_t address, uint32_t value) +{ + switch (offset) { + case MBX_MBR_OFFSET: + opp->mailboxes[n_mbx].mbr = value; + break; + case MBX_IVPR_OFFSET: + write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP, value); + break; + case MBX_DMR_OFFSET: + write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE, value); + break; + } +} +#endif +#endif /* 0 : Code provision for Intel model */ + +static void openpic_gbl_write (void *opaque, uint32_t addr, uint32_t val) +{ + openpic_t *opp = opaque; + + DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); + if (addr & 0xF) + return; +#if defined OPENPIC_SWAP + val = bswap32(val); +#endif + addr &= 0xFF; + switch (addr) { + case 0x00: /* FREP */ + break; + case 0x20: /* GLBC */ + if (val & 0x80000000) + openpic_reset(opp); + opp->glbc = val & ~0x80000000; + break; + case 0x80: /* VENI */ + break; + case 0x90: /* PINT */ + /* XXX: Should be able to reset any CPU */ + if (val & 1) { + DPRINTF("Reset CPU IRQ\n"); + // cpu_interrupt(first_cpu, CPU_INTERRUPT_RESET); + } + break; +#if MAX_IPI > 0 + case 0xA0: /* IPI_IPVP */ + case 0xB0: + case 0xC0: + case 0xD0: + { + int idx; + idx = (addr - 0xA0) >> 4; + write_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IPVP, val); + } + break; +#endif + case 0xE0: /* SPVE */ + opp->spve = val & 0x000000FF; + break; + case 0xF0: /* TIFR */ + opp->tifr = val; + break; + default: + break; + } +} + +static uint32_t openpic_gbl_read (void *opaque, uint32_t addr) +{ + openpic_t *opp = opaque; + uint32_t retval; + + DPRINTF("%s: addr %08x\n", __func__, addr); + retval = 0xFFFFFFFF; + if (addr & 0xF) + return retval; + addr &= 0xFF; + switch (addr) { + case 0x00: /* FREP */ + retval = opp->frep; + break; + case 0x20: /* GLBC */ + retval = opp->glbc; + break; + case 0x80: /* VENI */ + retval = opp->veni; + break; + case 0x90: /* PINT */ + retval = 0x00000000; + break; +#if MAX_IPI > 0 + case 0xA0: /* IPI_IPVP */ + case 0xB0: + case 0xC0: + case 0xD0: + { + int idx; + idx = (addr - 0xA0) >> 4; + retval = read_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IPVP); + } + break; +#endif + case 0xE0: /* SPVE */ + retval = opp->spve; + break; + case 0xF0: /* TIFR */ + retval = opp->tifr; + break; + default: + break; + } + DPRINTF("%s: => %08x\n", __func__, retval); +#if defined OPENPIC_SWAP + retval = bswap32(retval); +#endif + + return retval; +} + +static void openpic_timer_write (void *opaque, uint32_t addr, uint32_t val) +{ + openpic_t *opp = opaque; + int idx; + + DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); + if (addr & 0xF) + return; +#if defined OPENPIC_SWAP + val = bswap32(val); +#endif + addr -= 0x1100; + addr &= 0xFFFF; + idx = (addr & 0xFFF0) >> 6; + addr = addr & 0x30; + switch (addr) { + case 0x00: /* TICC */ + break; + case 0x10: /* TIBC */ + if ((opp->timers[idx].ticc & 0x80000000) != 0 && + (val & 0x80000000) == 0 && + (opp->timers[idx].tibc & 0x80000000) != 0) + opp->timers[idx].ticc &= ~0x80000000; + opp->timers[idx].tibc = val; + break; + case 0x20: /* TIVP */ + write_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IPVP, val); + break; + case 0x30: /* TIDE */ + write_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IDE, val); + break; + } +} + +static uint32_t openpic_timer_read (void *opaque, uint32_t addr) +{ + openpic_t *opp = opaque; + uint32_t retval; + int idx; + + DPRINTF("%s: addr %08x\n", __func__, addr); + retval = 0xFFFFFFFF; + if (addr & 0xF) + return retval; + addr -= 0x1100; + addr &= 0xFFFF; + idx = (addr & 0xFFF0) >> 6; + addr = addr & 0x30; + switch (addr) { + case 0x00: /* TICC */ + retval = opp->timers[idx].ticc; + break; + case 0x10: /* TIBC */ + retval = opp->timers[idx].tibc; + break; + case 0x20: /* TIPV */ + retval = read_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IPVP); + break; + case 0x30: /* TIDE */ + retval = read_IRQreg(opp, IRQ_TIM0 + idx, IRQ_IDE); + break; + } + DPRINTF("%s: => %08x\n", __func__, retval); +#if defined OPENPIC_SWAP + retval = bswap32(retval); +#endif + + return retval; +} + +static void openpic_src_write (void *opaque, uint32_t addr, uint32_t val) +{ + openpic_t *opp = opaque; + int idx; + + DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); + if (addr & 0xF) + return; +#if defined OPENPIC_SWAP + val = tswap32(val); +#endif + addr = addr & 0xFFF0; + idx = addr >> 5; + if (addr & 0x10) { + /* EXDE / IFEDE / IEEDE */ + write_IRQreg(opp, idx, IRQ_IDE, val); + } else { + /* EXVP / IFEVP / IEEVP */ + write_IRQreg(opp, idx, IRQ_IPVP, val); + } +} + +static uint32_t openpic_src_read (void *opaque, uint32_t addr) +{ + openpic_t *opp = opaque; + uint32_t retval; + int idx; + + DPRINTF("%s: addr %08x\n", __func__, addr); + retval = 0xFFFFFFFF; + if (addr & 0xF) + return retval; + addr = addr & 0xFFF0; + idx = addr >> 5; + if (addr & 0x10) { + /* EXDE / IFEDE / IEEDE */ + retval = read_IRQreg(opp, idx, IRQ_IDE); + } else { + /* EXVP / IFEVP / IEEVP */ + retval = read_IRQreg(opp, idx, IRQ_IPVP); + } + DPRINTF("%s: => %08x\n", __func__, retval); +#if defined OPENPIC_SWAP + retval = tswap32(retval); +#endif + + return retval; +} + +static void openpic_cpu_write (void *opaque, uint32_t addr, uint32_t val) +{ + openpic_t *opp = opaque; + IRQ_src_t *src; + IRQ_dst_t *dst; + int idx, n_IRQ; + + DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); + if (addr & 0xF) + return; +#if defined OPENPIC_SWAP + val = bswap32(val); +#endif + addr &= 0x1FFF0; + idx = addr / 0x1000; + dst = &opp->dst[idx]; + addr &= 0xFF0; + switch (addr) { +#if MAX_IPI > 0 + case 0x40: /* PIPD */ + case 0x50: + case 0x60: + case 0x70: + idx = (addr - 0x40) >> 4; + write_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IDE, val); + openpic_set_irq(opp, IRQ_IPI0 + idx, 1); + openpic_set_irq(opp, IRQ_IPI0 + idx, 0); + break; +#endif + case 0x80: /* PCTP */ + dst->pctp = val & 0x0000000F; + break; + case 0x90: /* WHOAMI */ + /* Read-only register */ + break; + case 0xA0: /* PIAC */ + /* Read-only register */ + break; + case 0xB0: /* PEOI */ + DPRINTF("PEOI\n"); + n_IRQ = IRQ_get_next(opp, &dst->servicing); + IRQ_resetbit(&dst->servicing, n_IRQ); + dst->servicing.next = -1; + src = &opp->src[n_IRQ]; + /* Set up next servicing IRQ */ + IRQ_get_next(opp, &dst->servicing); + /* Check queued interrupts. */ + n_IRQ = IRQ_get_next(opp, &dst->raised); + if (n_IRQ != -1) { + src = &opp->src[n_IRQ]; + if (IPVP_PRIORITY(src->ipvp) > dst->servicing.priority) { + DPRINTF("Raise CPU IRQ\n"); + cpu_interrupt(dst->env, CPU_INTERRUPT_HARD); + } + } + break; + default: + break; + } +} + +static uint32_t openpic_cpu_read (void *opaque, uint32_t addr) +{ + openpic_t *opp = opaque; + IRQ_src_t *src; + IRQ_dst_t *dst; + uint32_t retval; + int idx, n_IRQ; + + DPRINTF("%s: addr %08x\n", __func__, addr); + retval = 0xFFFFFFFF; + if (addr & 0xF) + return retval; + addr &= 0x1FFF0; + idx = addr / 0x1000; + dst = &opp->dst[idx]; + addr &= 0xFF0; + switch (addr) { + case 0x80: /* PCTP */ + retval = dst->pctp; + break; + case 0x90: /* WHOAMI */ + retval = idx; + break; + case 0xA0: /* PIAC */ + n_IRQ = IRQ_get_next(opp, &dst->raised); + DPRINTF("PIAC: irq=%d\n", n_IRQ); + if (n_IRQ == -1) { + /* No more interrupt pending */ + retval = opp->spve; + } else { + src = &opp->src[n_IRQ]; + if (!test_bit(&src->ipvp, IPVP_ACTIVITY) || + !(IPVP_PRIORITY(src->ipvp) > dst->pctp)) { + /* - Spurious level-sensitive IRQ + * - Priorities has been changed + * and the pending IRQ isn't allowed anymore + */ + reset_bit(&src->ipvp, IPVP_ACTIVITY); + retval = IPVP_VECTOR(opp->spve); + } else { + /* IRQ enter servicing state */ + IRQ_setbit(&dst->servicing, n_IRQ); + retval = IPVP_VECTOR(src->ipvp); + } + IRQ_resetbit(&dst->raised, n_IRQ); + dst->raised.next = -1; + if (!test_bit(&src->ipvp, IPVP_SENSE)) { + /* edge-sensitive IRQ */ + reset_bit(&src->ipvp, IPVP_ACTIVITY); + src->pending = 0; + } + } + break; + case 0xB0: /* PEOI */ + retval = 0; + break; +#if MAX_IPI > 0 + case 0x40: /* IDE */ + case 0x50: + idx = (addr - 0x40) >> 4; + retval = read_IRQreg(opp, IRQ_IPI0 + idx, IRQ_IDE); + break; +#endif + default: + break; + } + DPRINTF("%s: => %08x\n", __func__, retval); +#if defined OPENPIC_SWAP + retval= bswap32(retval); +#endif + + return retval; +} + +static void openpic_buggy_write (void *opaque, + target_phys_addr_t addr, uint32_t val) +{ + printf("Invalid OPENPIC write access !\n"); +} + +static uint32_t openpic_buggy_read (void *opaque, target_phys_addr_t addr) +{ + printf("Invalid OPENPIC read access !\n"); + + return -1; +} + +static void openpic_writel (void *opaque, + target_phys_addr_t addr, uint32_t val) +{ + openpic_t *opp = opaque; + + addr &= 0x3FFFF; + DPRINTF("%s: offset %08x val: %08x\n", __func__, (int)addr, val); + if (addr < 0x1100) { + /* Global registers */ + openpic_gbl_write(opp, addr, val); + } else if (addr < 0x10000) { + /* Timers registers */ + openpic_timer_write(opp, addr, val); + } else if (addr < 0x20000) { + /* Source registers */ + openpic_src_write(opp, addr, val); + } else { + /* CPU registers */ + openpic_cpu_write(opp, addr, val); + } +} + +static uint32_t openpic_readl (void *opaque,target_phys_addr_t addr) +{ + openpic_t *opp = opaque; + uint32_t retval; + + addr &= 0x3FFFF; + DPRINTF("%s: offset %08x\n", __func__, (int)addr); + if (addr < 0x1100) { + /* Global registers */ + retval = openpic_gbl_read(opp, addr); + } else if (addr < 0x10000) { + /* Timers registers */ + retval = openpic_timer_read(opp, addr); + } else if (addr < 0x20000) { + /* Source registers */ + retval = openpic_src_read(opp, addr); + } else { + /* CPU registers */ + retval = openpic_cpu_read(opp, addr); + } + + return retval; +} + +static CPUWriteMemoryFunc *openpic_write[] = { + &openpic_buggy_write, + &openpic_buggy_write, + &openpic_writel, +}; + +static CPUReadMemoryFunc *openpic_read[] = { + &openpic_buggy_read, + &openpic_buggy_read, + &openpic_readl, +}; + +static void openpic_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + openpic_t *opp; + + DPRINTF("Map OpenPIC\n"); + opp = (openpic_t *)pci_dev; + /* Global registers */ + DPRINTF("Register OPENPIC gbl %08x => %08x\n", + addr + 0x1000, addr + 0x1000 + 0x100); + /* Timer registers */ + DPRINTF("Register OPENPIC timer %08x => %08x\n", + addr + 0x1100, addr + 0x1100 + 0x40 * MAX_TMR); + /* Interrupt source registers */ + DPRINTF("Register OPENPIC src %08x => %08x\n", + addr + 0x10000, addr + 0x10000 + 0x20 * (EXT_IRQ + 2)); + /* Per CPU registers */ + DPRINTF("Register OPENPIC dst %08x => %08x\n", + addr + 0x20000, addr + 0x20000 + 0x1000 * MAX_CPU); + cpu_register_physical_memory(addr, 0x40000, opp->mem_index); +#if 0 // Don't implement ISU for now + opp_io_memory = cpu_register_io_memory(0, openpic_src_read, + openpic_src_write); + cpu_register_physical_memory(isu_base, 0x20 * (EXT_IRQ + 2), + opp_io_memory); +#endif +} + +openpic_t *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus, + CPUPPCState **envp) +{ + openpic_t *opp; + uint8_t *pci_conf; + int i, m; + + /* XXX: for now, only one CPU is supported */ + if (nb_cpus != 1) + return NULL; + if (bus) { + opp = (openpic_t *)pci_register_device(bus, "OpenPIC", sizeof(openpic_t), + -1, NULL, NULL); + if (opp == NULL) + return NULL; + pci_conf = opp->pci_dev.config; + pci_conf[0x00] = 0x14; // IBM MPIC2 + pci_conf[0x01] = 0x10; + pci_conf[0x02] = 0xFF; + pci_conf[0x03] = 0xFF; + pci_conf[0x0a] = 0x80; // PIC + pci_conf[0x0b] = 0x08; + pci_conf[0x0e] = 0x00; // header_type + pci_conf[0x3d] = 0x00; // no interrupt pin + + /* Register I/O spaces */ + pci_register_io_region((PCIDevice *)opp, 0, 0x40000, + PCI_ADDRESS_SPACE_MEM, &openpic_map); + } else { + opp = qemu_mallocz(sizeof(openpic_t)); + } + + opp->mem_index = cpu_register_io_memory(0, openpic_read, + openpic_write, opp); + + // isu_base &= 0xFFFC0000; + opp->nb_cpus = nb_cpus; + /* Set IRQ types */ + for (i = 0; i < EXT_IRQ; i++) { + opp->src[i].type = IRQ_EXTERNAL; + } + for (; i < IRQ_TIM0; i++) { + opp->src[i].type = IRQ_SPECIAL; + } +#if MAX_IPI > 0 + m = IRQ_IPI0; +#else + m = IRQ_DBL0; +#endif + for (; i < m; i++) { + opp->src[i].type = IRQ_TIMER; + } + for (; i < MAX_IRQ; i++) { + opp->src[i].type = IRQ_INTERNAL; + } + for (i = 0; i < nb_cpus; i++) + opp->dst[i].env = envp[i]; + openpic_reset(opp); + if (pmem_index) + *pmem_index = opp->mem_index; + return opp; +} diff --git a/tools/ioemu/hw/parallel.c b/tools/ioemu/hw/parallel.c new file mode 100644 index 0000000000..cba95610ef --- /dev/null +++ b/tools/ioemu/hw/parallel.c @@ -0,0 +1,183 @@ +/* + * QEMU Parallel PORT emulation + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG_PARALLEL + +/* + * These are the definitions for the Printer Status Register + */ +#define PARA_STS_BUSY 0x80 /* Busy complement */ +#define PARA_STS_ACK 0x40 /* Acknowledge */ +#define PARA_STS_PAPER 0x20 /* Out of paper */ +#define PARA_STS_ONLINE 0x10 /* Online */ +#define PARA_STS_ERROR 0x08 /* Error complement */ + +/* + * These are the definitions for the Printer Control Register + */ +#define PARA_CTR_INTEN 0x10 /* IRQ Enable */ +#define PARA_CTR_SELECT 0x08 /* Select In complement */ +#define PARA_CTR_INIT 0x04 /* Initialize Printer complement */ +#define PARA_CTR_AUTOLF 0x02 /* Auto linefeed complement */ +#define PARA_CTR_STROBE 0x01 /* Strobe complement */ + +struct ParallelState { + uint8_t data; + uint8_t status; /* read only register */ + uint8_t control; + int irq; + int irq_pending; + CharDriverState *chr; + int hw_driver; +}; + +static void parallel_update_irq(ParallelState *s) +{ + if (s->irq_pending) + pic_set_irq(s->irq, 1); + else + pic_set_irq(s->irq, 0); +} + +static void parallel_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + ParallelState *s = opaque; + + addr &= 7; +#ifdef DEBUG_PARALLEL + printf("parallel: write addr=0x%02x val=0x%02x\n", addr, val); +#endif + switch(addr) { + case 0: + if (s->hw_driver) { + s->data = val; + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_DATA, &s->data); + } else { + s->data = val; + parallel_update_irq(s); + } + break; + case 2: + if (s->hw_driver) { + s->control = val; + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &s->control); + } else { + if ((val & PARA_CTR_INIT) == 0 ) { + s->status = PARA_STS_BUSY; + s->status |= PARA_STS_ACK; + s->status |= PARA_STS_ONLINE; + s->status |= PARA_STS_ERROR; + } + else if (val & PARA_CTR_SELECT) { + if (val & PARA_CTR_STROBE) { + s->status &= ~PARA_STS_BUSY; + if ((s->control & PARA_CTR_STROBE) == 0) + qemu_chr_write(s->chr, &s->data, 1); + } else { + if (s->control & PARA_CTR_INTEN) { + s->irq_pending = 1; + } + } + } + parallel_update_irq(s); + s->control = val; + } + break; + } +} + +static uint32_t parallel_ioport_read(void *opaque, uint32_t addr) +{ + ParallelState *s = opaque; + uint32_t ret = 0xff; + + addr &= 7; + switch(addr) { + case 0: + if (s->hw_driver) { + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &s->data); + } + ret = s->data; + break; + case 1: + if (s->hw_driver) { + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &s->status); + ret = s->status; + } else { + ret = s->status; + s->irq_pending = 0; + if ((s->status & PARA_STS_BUSY) == 0 && (s->control & PARA_CTR_STROBE) == 0) { + /* XXX Fixme: wait 5 microseconds */ + if (s->status & PARA_STS_ACK) + s->status &= ~PARA_STS_ACK; + else { + /* XXX Fixme: wait 5 microseconds */ + s->status |= PARA_STS_ACK; + s->status |= PARA_STS_BUSY; + } + } + parallel_update_irq(s); + } + break; + case 2: + if (s->hw_driver) { + qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &s->control); + } + ret = s->control; + break; + } +#ifdef DEBUG_PARALLEL + printf("parallel: read addr=0x%02x val=0x%02x\n", addr, ret); +#endif + return ret; +} + +/* If fd is zero, it means that the parallel device uses the console */ +ParallelState *parallel_init(int base, int irq, CharDriverState *chr) +{ + ParallelState *s; + uint8_t dummy; + + s = qemu_mallocz(sizeof(ParallelState)); + if (!s) + return NULL; + s->chr = chr; + s->hw_driver = 0; + if (qemu_chr_ioctl(chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) + s->hw_driver = 1; + + s->irq = irq; + s->data = 0; + s->status = PARA_STS_BUSY; + s->status |= PARA_STS_ACK; + s->status |= PARA_STS_ONLINE; + s->status |= PARA_STS_ERROR; + s->control = PARA_CTR_SELECT; + s->control |= PARA_CTR_INIT; + + register_ioport_write(base, 8, 1, parallel_ioport_write, s); + register_ioport_read(base, 8, 1, parallel_ioport_read, s); + return s; +} diff --git a/tools/ioemu/hw/pc.c b/tools/ioemu/hw/pc.c new file mode 100644 index 0000000000..3a7b021e0c --- /dev/null +++ b/tools/ioemu/hw/pc.c @@ -0,0 +1,927 @@ +/* + * QEMU PC System Emulator + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* output Bochs bios info messages */ +//#define DEBUG_BIOS + +#define BIOS_FILENAME "bios.bin" +#define VGABIOS_FILENAME "vgabios.bin" +#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin" +#define LINUX_BOOT_FILENAME "linux_boot.bin" + +#define KERNEL_LOAD_ADDR 0x00100000 +#define INITRD_LOAD_ADDR 0x00600000 +#define KERNEL_PARAMS_ADDR 0x00090000 +#define KERNEL_CMDLINE_ADDR 0x00099000 + +static fdctrl_t *floppy_controller; +static RTCState *rtc_state; +#ifndef CONFIG_DM +static PITState *pit; +#endif /* !CONFIG_DM */ +#ifndef CONFIG_DM +static IOAPICState *ioapic; +#endif /* !CONFIG_DM */ +static USBPort *usb_root_ports[2]; + +static void ioport80_write(void *opaque, uint32_t addr, uint32_t data) +{ +} + +/* MSDOS compatibility mode FPU exception support */ +/* XXX: add IGNNE support */ +void cpu_set_ferr(CPUX86State *s) +{ + pic_set_irq(13, 1); +} + +static void ioportF0_write(void *opaque, uint32_t addr, uint32_t data) +{ + pic_set_irq(13, 0); +} + +/* TSC handling */ + +uint64_t cpu_get_tsc(CPUX86State *env) +{ + return qemu_get_clock(vm_clock); +} + +#ifndef CONFIG_DM +/* IRQ handling */ +int cpu_get_pic_interrupt(CPUState *env) +{ + int intno; + + intno = apic_get_interrupt(env); + if (intno >= 0) { + /* set irq request if a PIC irq is still pending */ + /* XXX: improve that */ + pic_update_irq(isa_pic); + return intno; + } + /* read the irq from the PIC */ + intno = pic_read_irq(isa_pic); + return intno; +} +#endif /* CONFIG_DM */ + +static void pic_irq_request(void *opaque, int level) +{ + CPUState *env = opaque; + if (level) + cpu_interrupt(env, CPU_INTERRUPT_HARD); + else + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); +} + +/* PC cmos mappings */ + +#define REG_EQUIPMENT_BYTE 0x14 +#define REG_IBM_CENTURY_BYTE 0x32 +#define REG_IBM_PS2_CENTURY_BYTE 0x37 + + +static inline int to_bcd(RTCState *s, int a) +{ + return ((a / 10) << 4) | (a % 10); +} + +static int cmos_get_fd_drive_type(int fd0) +{ + int val; + + switch (fd0) { + case 0: + /* 1.44 Mb 3"5 drive */ + val = 4; + break; + case 1: + /* 2.88 Mb 3"5 drive */ + val = 5; + break; + case 2: + /* 1.2 Mb 5"5 drive */ + val = 2; + break; + default: + val = 0; + break; + } + return val; +} + +static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd) +{ + RTCState *s = rtc_state; + int cylinders, heads, sectors; + bdrv_get_geometry_hint(hd, &cylinders, &heads, §ors); + rtc_set_memory(s, type_ofs, 47); + rtc_set_memory(s, info_ofs, cylinders); + rtc_set_memory(s, info_ofs + 1, cylinders >> 8); + rtc_set_memory(s, info_ofs + 2, heads); + rtc_set_memory(s, info_ofs + 3, 0xff); + rtc_set_memory(s, info_ofs + 4, 0xff); + rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3)); + rtc_set_memory(s, info_ofs + 6, cylinders); + rtc_set_memory(s, info_ofs + 7, cylinders >> 8); + rtc_set_memory(s, info_ofs + 8, sectors); +} + +/* hd_table must contain 4 block drivers */ +static void cmos_init(uint64_t ram_size, int boot_device, BlockDriverState **hd_table, time_t timeoffset) +{ + RTCState *s = rtc_state; + int val; + int fd0, fd1, nb; + time_t ti; + struct tm *tm; + int i; + + /* set the CMOS date */ + time(&ti); + ti += timeoffset; + if (rtc_utc) + tm = gmtime(&ti); + else + tm = localtime(&ti); + rtc_set_date(s, tm); + + val = to_bcd(s, (tm->tm_year / 100) + 19); + rtc_set_memory(s, REG_IBM_CENTURY_BYTE, val); + rtc_set_memory(s, REG_IBM_PS2_CENTURY_BYTE, val); + + /* various important CMOS locations needed by PC/Bochs bios */ + + /* memory size */ + val = 640; /* base memory in K */ + rtc_set_memory(s, 0x15, val); + rtc_set_memory(s, 0x16, val >> 8); + + val = (ram_size / 1024) - 1024; + if (val > 65535) + val = 65535; + rtc_set_memory(s, 0x17, val); + rtc_set_memory(s, 0x18, val >> 8); + rtc_set_memory(s, 0x30, val); + rtc_set_memory(s, 0x31, val >> 8); + + if (ram_size > (16 * 1024 * 1024)) + val = (ram_size / 65536) - ((16 * 1024 * 1024) / 65536); + else + val = 0; + if (val > 65535) + val = 65535; + rtc_set_memory(s, 0x34, val); + rtc_set_memory(s, 0x35, val >> 8); + + switch(boot_device) { + case 'a': + case 'b': + rtc_set_memory(s, 0x3d, 0x01); /* floppy boot */ + break; + default: + case 'c': + rtc_set_memory(s, 0x3d, 0x02); /* hard drive boot */ + break; + case 'd': + rtc_set_memory(s, 0x3d, 0x03); /* CD-ROM boot */ + break; + } + + /* floppy type */ + + fd0 = fdctrl_get_drive_type(floppy_controller, 0); + fd1 = fdctrl_get_drive_type(floppy_controller, 1); + + val = (cmos_get_fd_drive_type(fd0) << 4) | cmos_get_fd_drive_type(fd1); + rtc_set_memory(s, 0x10, val); + + val = 0; + nb = 0; + if (fd0 < 3) + nb++; + if (fd1 < 3) + nb++; + switch (nb) { + case 0: + break; + case 1: + val |= 0x01; /* 1 drive, ready for boot */ + break; + case 2: + val |= 0x41; /* 2 drives, ready for boot */ + break; + } + val |= 0x02; /* FPU is there */ + val |= 0x04; /* PS/2 mouse installed */ + rtc_set_memory(s, REG_EQUIPMENT_BYTE, val); + + /* hard drives */ + + rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0)); + if (hd_table[0]) + cmos_init_hd(0x19, 0x1b, hd_table[0]); + if (hd_table[1]) + cmos_init_hd(0x1a, 0x24, hd_table[1]); + + val = 0; + for (i = 0; i < 4; i++) { + if (hd_table[i]) { + int cylinders, heads, sectors, translation; + /* NOTE: bdrv_get_geometry_hint() returns the physical + geometry. It is always such that: 1 <= sects <= 63, 1 + <= heads <= 16, 1 <= cylinders <= 16383. The BIOS + geometry can be different if a translation is done. */ + translation = bdrv_get_translation_hint(hd_table[i]); + if (translation == BIOS_ATA_TRANSLATION_AUTO) { + bdrv_get_geometry_hint(hd_table[i], &cylinders, &heads, §ors); + if (cylinders <= 1024 && heads <= 16 && sectors <= 63) { + /* No translation. */ + translation = 0; + } else { + /* LBA translation. */ + translation = 1; + } + } else { + translation--; + } + val |= translation << (i * 2); + } + } + rtc_set_memory(s, 0x39, val); + + /* Disable check of 0x55AA signature on the last two bytes of + first sector of disk. XXX: make it the default ? */ + // rtc_set_memory(s, 0x38, 1); +} + +void ioport_set_a20(int enable) +{ + /* XXX: send to all CPUs ? */ + cpu_x86_set_a20(first_cpu, enable); +} + +int ioport_get_a20(void) +{ + return ((first_cpu->a20_mask >> 20) & 1); +} + +static void ioport92_write(void *opaque, uint32_t addr, uint32_t val) +{ + ioport_set_a20((val >> 1) & 1); + /* XXX: bit 0 is fast reset */ +} + +static uint32_t ioport92_read(void *opaque, uint32_t addr) +{ + return ioport_get_a20() << 1; +} + +/***********************************************************/ +/* Bochs BIOS debug ports */ + +void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val) +{ + static const char shutdown_str[8] = "Shutdown"; + static int shutdown_index = 0; + + switch(addr) { + /* Bochs BIOS messages */ + case 0x400: + case 0x401: + fprintf(stderr, "BIOS panic at rombios.c, line %d\n", val); + exit(1); + case 0x402: + case 0x403: +#ifdef DEBUG_BIOS + fprintf(stderr, "%c", val); +#endif + break; + case 0x8900: + /* same as Bochs power off */ + if (val == shutdown_str[shutdown_index]) { + shutdown_index++; + if (shutdown_index == 8) { + shutdown_index = 0; + qemu_system_shutdown_request(); + } + } else { + shutdown_index = 0; + } + break; + + /* LGPL'ed VGA BIOS messages */ + case 0x501: + case 0x502: + fprintf(stderr, "VGA BIOS panic, line %d\n", val); + exit(1); + case 0x500: + case 0x503: +#ifdef DEBUG_BIOS + fprintf(stderr, "%c", val); +#endif + break; + } +} + +void bochs_bios_init(void) +{ + register_ioport_write(0x400, 1, 2, bochs_bios_write, NULL); + register_ioport_write(0x401, 1, 2, bochs_bios_write, NULL); + register_ioport_write(0x402, 1, 1, bochs_bios_write, NULL); + register_ioport_write(0x403, 1, 1, bochs_bios_write, NULL); + register_ioport_write(0x8900, 1, 1, bochs_bios_write, NULL); + + register_ioport_write(0x501, 1, 2, bochs_bios_write, NULL); + register_ioport_write(0x502, 1, 2, bochs_bios_write, NULL); + register_ioport_write(0x500, 1, 1, bochs_bios_write, NULL); + register_ioport_write(0x503, 1, 1, bochs_bios_write, NULL); +} + + +int load_kernel(const char *filename, uint8_t *addr, + uint8_t *real_addr) +{ + int fd, size; + int setup_sects; + + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) + return -1; + + /* load 16 bit code */ + if (read(fd, real_addr, 512) != 512) + goto fail; + setup_sects = real_addr[0x1F1]; + if (!setup_sects) + setup_sects = 4; + if (read(fd, real_addr + 512, setup_sects * 512) != + setup_sects * 512) + goto fail; + + /* load 32 bit code */ + size = read(fd, addr, 16 * 1024 * 1024); + if (size < 0) + goto fail; + close(fd); + return size; + fail: + close(fd); + return -1; +} + +static void main_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + cpu_reset(env); +} + +/*************************************************/ + +#ifndef CONFIG_DM +static void putb(uint8_t **pp, int val) +{ + uint8_t *q; + q = *pp; + *q++ = val; + *pp = q; +} + +static void putstr(uint8_t **pp, const char *str) +{ + uint8_t *q; + q = *pp; + while (*str) + *q++ = *str++; + *pp = q; +} + +static void putle16(uint8_t **pp, int val) +{ + uint8_t *q; + q = *pp; + *q++ = val; + *q++ = val >> 8; + *pp = q; +} + +static void putle32(uint8_t **pp, int val) +{ + uint8_t *q; + q = *pp; + *q++ = val; + *q++ = val >> 8; + *q++ = val >> 16; + *q++ = val >> 24; + *pp = q; +} + +static int mpf_checksum(const uint8_t *data, int len) +{ + int sum, i; + sum = 0; + for(i = 0; i < len; i++) + sum += data[i]; + return sum & 0xff; +} + +/* Build the Multi Processor table in the BIOS. Same values as Bochs. */ +static void bios_add_mptable(uint8_t *bios_data) +{ + uint8_t *mp_config_table, *q, *float_pointer_struct; + int ioapic_id, offset, i, len; + + if (smp_cpus <= 1) + return; + + mp_config_table = bios_data + 0xb000; + q = mp_config_table; + putstr(&q, "PCMP"); /* "PCMP signature */ + putle16(&q, 0); /* table length (patched later) */ + putb(&q, 4); /* spec rev */ + putb(&q, 0); /* checksum (patched later) */ + putstr(&q, "QEMUCPU "); /* OEM id */ + putstr(&q, "0.1 "); /* vendor id */ + putle32(&q, 0); /* OEM table ptr */ + putle16(&q, 0); /* OEM table size */ + putle16(&q, 20); /* entry count */ + putle32(&q, 0xfee00000); /* local APIC addr */ + putle16(&q, 0); /* ext table length */ + putb(&q, 0); /* ext table checksum */ + putb(&q, 0); /* reserved */ + + for(i = 0; i < smp_cpus; i++) { + putb(&q, 0); /* entry type = processor */ + putb(&q, i); /* APIC id */ + putb(&q, 0x11); /* local APIC version number */ + if (i == 0) + putb(&q, 3); /* cpu flags: enabled, bootstrap cpu */ + else + putb(&q, 1); /* cpu flags: enabled */ + putb(&q, 0); /* cpu signature */ + putb(&q, 6); + putb(&q, 0); + putb(&q, 0); + putle16(&q, 0x201); /* feature flags */ + putle16(&q, 0); + + putle16(&q, 0); /* reserved */ + putle16(&q, 0); + putle16(&q, 0); + putle16(&q, 0); + } + + /* isa bus */ + putb(&q, 1); /* entry type = bus */ + putb(&q, 0); /* bus ID */ + putstr(&q, "ISA "); + + /* ioapic */ + ioapic_id = smp_cpus; + putb(&q, 2); /* entry type = I/O APIC */ + putb(&q, ioapic_id); /* apic ID */ + putb(&q, 0x11); /* I/O APIC version number */ + putb(&q, 1); /* enable */ + putle32(&q, 0xfec00000); /* I/O APIC addr */ + + /* irqs */ + for(i = 0; i < 16; i++) { + putb(&q, 3); /* entry type = I/O interrupt */ + putb(&q, 0); /* interrupt type = vectored interrupt */ + putb(&q, 0); /* flags: po=0, el=0 */ + putb(&q, 0); + putb(&q, 0); /* source bus ID = ISA */ + putb(&q, i); /* source bus IRQ */ + putb(&q, ioapic_id); /* dest I/O APIC ID */ + putb(&q, i); /* dest I/O APIC interrupt in */ + } + /* patch length */ + len = q - mp_config_table; + mp_config_table[4] = len; + mp_config_table[5] = len >> 8; + + mp_config_table[7] = -mpf_checksum(mp_config_table, q - mp_config_table); + + /* align to 16 */ + offset = q - bios_data; + offset = (offset + 15) & ~15; + float_pointer_struct = bios_data + offset; + + /* floating pointer structure */ + q = float_pointer_struct; + putstr(&q, "_MP_"); + /* pointer to MP config table */ + putle32(&q, mp_config_table - bios_data + 0x000f0000); + + putb(&q, 1); /* length in 16 byte units */ + putb(&q, 4); /* MP spec revision */ + putb(&q, 0); /* checksum (patched later) */ + putb(&q, 0); /* MP feature byte 1 */ + + putb(&q, 0); + putb(&q, 0); + putb(&q, 0); + putb(&q, 0); + float_pointer_struct[10] = + -mpf_checksum(float_pointer_struct, q - float_pointer_struct); +} +#endif /* !CONFIG_DM */ + + +static const int ide_iobase[2] = { 0x1f0, 0x170 }; +static const int ide_iobase2[2] = { 0x3f6, 0x376 }; +static const int ide_irq[2] = { 14, 15 }; + +#define NE2000_NB_MAX 6 + +static int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 }; +static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 }; + +static int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; +static int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 }; + +static int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc }; +static int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 }; + +/* PIIX4 acpi pci configuration space, func 3 */ +extern void pci_piix4_acpi_init(PCIBus *bus); + +#ifdef HAS_AUDIO +static void audio_init (PCIBus *pci_bus) +{ + struct soundhw *c; + int audio_enabled = 0; + + for (c = soundhw; !audio_enabled && c->name; ++c) { + audio_enabled = c->enabled; + } + + if (audio_enabled) { + AudioState *s; + + s = AUD_init (); + if (s) { + for (c = soundhw; c->name; ++c) { + if (c->enabled) { + if (c->isa) { + c->init.init_isa (s); + } + else { + if (pci_bus) { + c->init.init_pci (pci_bus, s); + } + } + } + } + } + } +} +#endif + +static void pc_init_ne2k_isa(NICInfo *nd) +{ + static int nb_ne2k = 0; + + if (nb_ne2k == NE2000_NB_MAX) + return; + isa_ne2000_init(ne2000_io[nb_ne2k], ne2000_irq[nb_ne2k], nd); + nb_ne2k++; +} + +#define NOBIOS 1 + +/* PC hardware initialisation */ +static void pc_init1(uint64_t ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, time_t timeoffset, + int pci_enabled) +{ +#ifndef NOBIOS + char buf[1024]; + int ret, initrd_size; +#endif + int linux_boot, i; +#ifndef NOBIOS + unsigned long bios_offset, vga_bios_offset; + int bios_size, isa_bios_size; +#endif /* !NOBIOS */ + PCIBus *pci_bus; + CPUState *env; + NICInfo *nd; + + linux_boot = (kernel_filename != NULL); + + /* init CPUs */ + for(i = 0; i < smp_cpus; i++) { + env = cpu_init(); +#ifndef CONFIG_DM + if (i != 0) + env->hflags |= HF_HALTED_MASK; + if (smp_cpus > 1) { + /* XXX: enable it in all cases */ + env->cpuid_features |= CPUID_APIC; + } +#endif /* !CONFIG_DM */ + register_savevm("cpu", i, 3, cpu_save, cpu_load, env); + qemu_register_reset(main_cpu_reset, env); +#ifndef CONFIG_DM + if (pci_enabled) { + apic_init(env); + } +#endif /* !CONFIG_DM */ + } + + /* allocate RAM */ +#ifndef CONFIG_DM /* HVM domain owns memory */ + cpu_register_physical_memory(0, ram_size, 0); +#endif + +#ifndef NOBIOS + /* BIOS load */ + bios_offset = ram_size + vga_ram_size; + vga_bios_offset = bios_offset + 256 * 1024; + + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME); + bios_size = get_image_size(buf); + if (bios_size <= 0 || + (bios_size % 65536) != 0 || + bios_size > (256 * 1024)) { + goto bios_error; + } + ret = load_image(buf, phys_ram_base + bios_offset); + if (ret != bios_size) { + bios_error: + fprintf(stderr, "qemu: could not load PC bios '%s'\n", buf); + exit(1); + } + if (bios_size == 65536) { + bios_add_mptable(phys_ram_base + bios_offset); + } + + /* VGA BIOS load */ + if (cirrus_vga_enabled) { + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_CIRRUS_FILENAME); + } else { + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_FILENAME); + } + ret = load_image(buf, phys_ram_base + vga_bios_offset); +#endif /* !NOBIOS */ + + /* setup basic memory access */ +#ifndef CONFIG_DM /* HVM domain owns memory */ + cpu_register_physical_memory(0xc0000, 0x10000, + vga_bios_offset | IO_MEM_ROM); +#endif + +#ifndef NOBIOS + /* map the last 128KB of the BIOS in ISA space */ + isa_bios_size = bios_size; + if (isa_bios_size > (128 * 1024)) + isa_bios_size = 128 * 1024; + cpu_register_physical_memory(0xd0000, (192 * 1024) - isa_bios_size, + IO_MEM_UNASSIGNED); + cpu_register_physical_memory(0x100000 - isa_bios_size, + isa_bios_size, + (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM); + /* map all the bios at the top of memory */ + cpu_register_physical_memory((uint32_t)(-bios_size), + bios_size, bios_offset | IO_MEM_ROM); +#endif + + bochs_bios_init(); + +#ifndef CONFIG_DM + if (linux_boot) { + uint8_t bootsect[512]; + uint8_t old_bootsect[512]; + + if (bs_table[0] == NULL) { + fprintf(stderr, "A disk image must be given for 'hda' when booting a Linux kernel\n"); + exit(1); + } + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, LINUX_BOOT_FILENAME); + ret = load_image(buf, bootsect); + if (ret != sizeof(bootsect)) { + fprintf(stderr, "qemu: could not load linux boot sector '%s'\n", + buf); + exit(1); + } + + if (bdrv_read(bs_table[0], 0, old_bootsect, 1) >= 0) { + /* copy the MSDOS partition table */ + memcpy(bootsect + 0x1be, old_bootsect + 0x1be, 0x40); + } + + bdrv_set_boot_sector(bs_table[0], bootsect, sizeof(bootsect)); + + /* now we can load the kernel */ + ret = load_kernel(kernel_filename, + phys_ram_base + KERNEL_LOAD_ADDR, + phys_ram_base + KERNEL_PARAMS_ADDR); + if (ret < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + + /* load initrd */ + initrd_size = 0; + if (initrd_filename) { + initrd_size = load_image(initrd_filename, phys_ram_base + INITRD_LOAD_ADDR); + if (initrd_size < 0) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + } + if (initrd_size > 0) { + stl_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x218, INITRD_LOAD_ADDR); + stl_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x21c, initrd_size); + } + pstrcpy(phys_ram_base + KERNEL_CMDLINE_ADDR, 4096, + kernel_cmdline); + stw_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x20, 0xA33F); + stw_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x22, + KERNEL_CMDLINE_ADDR - KERNEL_PARAMS_ADDR); + /* loader type */ + stw_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x210, 0x01); + } +#endif /* !CONFIG_DM */ + + if (pci_enabled) { + pci_bus = i440fx_init(); + piix3_init(pci_bus); + } else { + pci_bus = NULL; + } + + /* init basic PC hardware */ + register_ioport_write(0x80, 1, 1, ioport80_write, NULL); + + register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL); + + if (cirrus_vga_enabled) { + if (pci_enabled) { + pci_cirrus_vga_init(pci_bus, + ds, NULL, ram_size, + vga_ram_size); + } else { + isa_cirrus_vga_init(ds, NULL, ram_size, + vga_ram_size); + } + } else { + vga_initialize(pci_bus, ds, NULL, ram_size, + vga_ram_size, 0, 0); + } + + rtc_state = rtc_init(0x70, 8); + + register_ioport_read(0x92, 1, 1, ioport92_read, NULL); + register_ioport_write(0x92, 1, 1, ioport92_write, NULL); + +#ifndef CONFIG_DM + if (pci_enabled) { + ioapic = ioapic_init(); + } +#endif /* !CONFIG_DM */ + isa_pic = pic_init(pic_irq_request, first_cpu); +#ifndef CONFIG_DM + pit = pit_init(0x40, 0); + pcspk_init(pit); +#endif /* !CONFIG_DM */ +#ifndef CONFIG_DM + if (pci_enabled) { + pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic); + } +#endif /* !CONFIG_DM */ + + for(i = 0; i < MAX_SERIAL_PORTS; i++) { + if (serial_hds[i]) { + serial_init(&pic_set_irq_new, isa_pic, + serial_io[i], serial_irq[i], serial_hds[i]); + } + } + + for(i = 0; i < MAX_PARALLEL_PORTS; i++) { + if (parallel_hds[i]) { + parallel_init(parallel_io[i], parallel_irq[i], parallel_hds[i]); + } + } + + for(i = 0; i < nb_nics; i++) { + nd = &nd_table[i]; + if (!nd->model) { + if (pci_enabled) { + nd->model = "ne2k_pci"; + } else { + nd->model = "ne2k_isa"; + } + } + if (strcmp(nd->model, "ne2k_isa") == 0) { + pc_init_ne2k_isa(nd); + } else if (pci_enabled) { + pci_nic_init(pci_bus, nd); + } else { + fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model); + exit(1); + } + } + + if (pci_enabled) { + pci_piix3_ide_init(pci_bus, bs_table); + } else { + for(i = 0; i < 2; i++) { + isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], + bs_table[2 * i], bs_table[2 * i + 1]); + } + } + + kbd_init(); + DMA_init(0); +#ifdef HAS_AUDIO + audio_init(pci_enabled ? pci_bus : NULL); +#endif + + floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table); + + cmos_init(ram_size, boot_device, bs_table, timeoffset); + + /* using PIIX4 acpi model */ + if (pci_enabled) + pci_piix4_acpi_init(pci_bus); + + if (pci_enabled && usb_enabled) { + usb_uhci_init(pci_bus, usb_root_ports); + usb_attach(usb_root_ports[0], vm_usb_hub); + } + + /* must be done after all PCI devices are instanciated */ + /* XXX: should be done in the Bochs BIOS */ + if (pci_enabled) { + pci_bios_init(); + } +} + +static void pc_init_pci(uint64_t ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, + int snapshot, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + time_t timeoffset) +{ + pc_init1(ram_size, vga_ram_size, boot_device, + ds, fd_filename, snapshot, + kernel_filename, kernel_cmdline, + initrd_filename, timeoffset, 1); +} + +static void pc_init_isa(uint64_t ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, + int snapshot, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + time_t timeoffset) +{ + pc_init1(ram_size, vga_ram_size, boot_device, + ds, fd_filename, snapshot, + kernel_filename, kernel_cmdline, + initrd_filename, timeoffset, 0); +} + +QEMUMachine pc_machine = { + "pc", + "Standard PC", + pc_init_pci, +}; + +QEMUMachine isapc_machine = { + "isapc", + "ISA-only PC", + pc_init_isa, +}; diff --git a/tools/ioemu/hw/pci.c b/tools/ioemu/hw/pci.c new file mode 100644 index 0000000000..096d0d1e05 --- /dev/null +++ b/tools/ioemu/hw/pci.c @@ -0,0 +1,1872 @@ +/* + * QEMU PCI bus manager + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG_PCI + +#define PCI_VENDOR_ID 0x00 /* 16 bits */ +#define PCI_DEVICE_ID 0x02 /* 16 bits */ +#define PCI_COMMAND 0x04 /* 16 bits */ +#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ +#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ +#define PCI_CLASS_DEVICE 0x0a /* Device class */ +#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ +#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ +#define PCI_MIN_GNT 0x3e /* 8 bits */ +#define PCI_MAX_LAT 0x3f /* 8 bits */ + +/* just used for simpler irq handling. */ +#define PCI_DEVICES_MAX 64 +#define PCI_IRQ_WORDS ((PCI_DEVICES_MAX + 31) / 32) + +struct PCIBus { + int bus_num; + int devfn_min; + void (*set_irq)(PCIDevice *pci_dev, int irq_num, int level); + uint32_t config_reg; /* XXX: suppress */ + /* low level pic */ + SetIRQFunc *low_set_irq; + void *irq_opaque; + PCIDevice *devices[256]; +}; + +target_phys_addr_t pci_mem_base; +static int pci_irq_index; +static uint32_t pci_irq_levels[4][PCI_IRQ_WORDS]; +static PCIBus *first_bus; + +static PCIBus *pci_register_bus(void) +{ + PCIBus *bus; + bus = qemu_mallocz(sizeof(PCIBus)); + first_bus = bus; + return bus; +} + +void generic_pci_save(QEMUFile* f, void *opaque) +{ + PCIDevice* s=(PCIDevice*)opaque; + + qemu_put_buffer(f, s->config, 256); +} + +int generic_pci_load(QEMUFile* f, void *opaque, int version_id) +{ + PCIDevice* s=(PCIDevice*)opaque; + + if (version_id != 1) + return -EINVAL; + + qemu_get_buffer(f, s->config, 256); + return 0; +} + +/* -1 for devfn means auto assign */ +PCIDevice *pci_register_device(PCIBus *bus, const char *name, + int instance_size, int devfn, + PCIConfigReadFunc *config_read, + PCIConfigWriteFunc *config_write) +{ + PCIDevice *pci_dev; + + if (pci_irq_index >= PCI_DEVICES_MAX) + return NULL; + + if (devfn < 0) { + for(devfn = bus->devfn_min ; devfn < 256; devfn += 8) { + if (!bus->devices[devfn]) + goto found; + } + return NULL; + found: ; + } + pci_dev = qemu_mallocz(instance_size); + if (!pci_dev) + return NULL; + pci_dev->bus = bus; + pci_dev->devfn = devfn; + pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); + + if (!config_read) + config_read = pci_default_read_config; + if (!config_write) + config_write = pci_default_write_config; + pci_dev->config_read = config_read; + pci_dev->config_write = config_write; + pci_dev->irq_index = pci_irq_index++; + bus->devices[devfn] = pci_dev; + return pci_dev; +} + +void pci_register_io_region(PCIDevice *pci_dev, int region_num, + uint32_t size, int type, + PCIMapIORegionFunc *map_func) +{ + PCIIORegion *r; + uint32_t addr; + + if ((unsigned int)region_num >= PCI_NUM_REGIONS) + return; + r = &pci_dev->io_regions[region_num]; + r->addr = -1; + r->size = size; + r->type = type; + r->map_func = map_func; + if (region_num == PCI_ROM_SLOT) { + addr = 0x30; + } else { + addr = 0x10 + region_num * 4; + } + *(uint32_t *)(pci_dev->config + addr) = cpu_to_le32(type); +} + +static void pci_addr_writel(void* opaque, uint32_t addr, uint32_t val) +{ + PCIBus *s = opaque; + s->config_reg = val; +} + +static uint32_t pci_addr_readl(void* opaque, uint32_t addr) +{ + PCIBus *s = opaque; + return s->config_reg; +} + +static void pci_update_mappings(PCIDevice *d) +{ + PCIIORegion *r; + int cmd, i; + uint32_t last_addr, new_addr, config_ofs; + + cmd = le16_to_cpu(*(uint16_t *)(d->config + PCI_COMMAND)); + for(i = 0; i < PCI_NUM_REGIONS; i++) { + r = &d->io_regions[i]; + if (i == PCI_ROM_SLOT) { + config_ofs = 0x30; + } else { + config_ofs = 0x10 + i * 4; + } + if (r->size != 0) { + if (r->type & PCI_ADDRESS_SPACE_IO) { + if (cmd & PCI_COMMAND_IO) { + new_addr = le32_to_cpu(*(uint32_t *)(d->config + + config_ofs)); + new_addr = new_addr & ~(r->size - 1); + last_addr = new_addr + r->size - 1; + /* NOTE: we have only 64K ioports on PC */ + if (last_addr <= new_addr || new_addr == 0 || + last_addr >= 0x10000) { + new_addr = -1; + } + } else { + new_addr = -1; + } + } else { + if (cmd & PCI_COMMAND_MEMORY) { + new_addr = le32_to_cpu(*(uint32_t *)(d->config + + config_ofs)); + /* the ROM slot has a specific enable bit */ + if (i == PCI_ROM_SLOT && !(new_addr & 1)) + goto no_mem_map; + new_addr = new_addr & ~(r->size - 1); + last_addr = new_addr + r->size - 1; + /* NOTE: we do not support wrapping */ + /* XXX: as we cannot support really dynamic + mappings, we handle specific values as invalid + mappings. */ + if (last_addr <= new_addr || new_addr == 0 || + last_addr == -1) { + new_addr = -1; + } + } else { + no_mem_map: + new_addr = -1; + } + } + /* now do the real mapping */ + if (new_addr != r->addr) { + if (r->addr != -1) { + if (r->type & PCI_ADDRESS_SPACE_IO) { + int class; + /* NOTE: specific hack for IDE in PC case: + only one byte must be mapped. */ + class = d->config[0x0a] | (d->config[0x0b] << 8); + if (class == 0x0101 && r->size == 4) { + isa_unassign_ioport(r->addr + 2, 1); + } else { + isa_unassign_ioport(r->addr, r->size); + } + } else { + cpu_register_physical_memory(r->addr + pci_mem_base, + r->size, + IO_MEM_UNASSIGNED); + } + } + r->addr = new_addr; + if (r->addr != -1) { + r->map_func(d, i, r->addr, r->size, r->type); + } + } + } + } +} + +uint32_t pci_default_read_config(PCIDevice *d, + uint32_t address, int len) +{ + uint32_t val; + switch(len) { + case 1: + val = d->config[address]; + break; + case 2: + val = le16_to_cpu(*(uint16_t *)(d->config + address)); + break; + default: + case 4: + val = le32_to_cpu(*(uint32_t *)(d->config + address)); + break; + } + return val; +} + +void pci_default_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + int can_write, i; + uint32_t end, addr; + + if (len == 4 && ((address >= 0x10 && address < 0x10 + 4 * 6) || + (address >= 0x30 && address < 0x34))) { + PCIIORegion *r; + int reg; + + if ( address >= 0x30 ) { + reg = PCI_ROM_SLOT; + }else{ + reg = (address - 0x10) >> 2; + } + r = &d->io_regions[reg]; + if (r->size == 0) + goto default_config; + /* compute the stored value */ + if (reg == PCI_ROM_SLOT) { + /* keep ROM enable bit */ + val &= (~(r->size - 1)) | 1; + } else { + val &= ~(r->size - 1); + val |= r->type; + } + *(uint32_t *)(d->config + address) = cpu_to_le32(val); + pci_update_mappings(d); + return; + } + default_config: + /* not efficient, but simple */ + addr = address; + for(i = 0; i < len; i++) { + /* default read/write accesses */ + switch(d->config[0x0e]) { + case 0x00: + case 0x80: + switch(addr) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0e: + case 0x10 ... 0x27: /* base */ + case 0x30 ... 0x33: /* rom */ + case 0x3d: + can_write = 0; + break; + default: + can_write = 1; + break; + } + break; + default: + case 0x01: + switch(addr) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0e: + case 0x38 ... 0x3b: /* rom */ + case 0x3d: + can_write = 0; + break; + default: + can_write = 1; + break; + } + break; + } + if (can_write) { + d->config[addr] = val; + } + addr++; + val >>= 8; + } + + end = address + len; + if (end > PCI_COMMAND && address < (PCI_COMMAND + 2)) { + /* if the command register is modified, we must modify the mappings */ + pci_update_mappings(d); + } +} + +static void pci_data_write(void *opaque, uint32_t addr, + uint32_t val, int len) +{ + PCIBus *s = opaque; + PCIDevice *pci_dev; + int config_addr, bus_num; + +#if defined(DEBUG_PCI) && 0 + printf("pci_data_write: addr=%08x val=%08x len=%d\n", + s->config_reg, val, len); +#endif + if (!(s->config_reg & (1 << 31))) { + return; + } + bus_num = (s->config_reg >> 16) & 0xff; + if (bus_num != 0) + return; + pci_dev = s->devices[(s->config_reg >> 8) & 0xff]; + if (!pci_dev) + return; + config_addr = (s->config_reg & 0xfc) | (addr & 3); +#if defined(DEBUG_PCI) + printf("pci_config_write: %s: addr=%02x val=%08x len=%d\n", + pci_dev->name, config_addr, val, len); +#endif + pci_dev->config_write(pci_dev, config_addr, val, len); +} + +static uint32_t pci_data_read(void *opaque, uint32_t addr, + int len) +{ + PCIBus *s = opaque; + PCIDevice *pci_dev; + int config_addr, bus_num; + uint32_t val; + + if (!(s->config_reg & (1 << 31))) + goto fail; + bus_num = (s->config_reg >> 16) & 0xff; + if (bus_num != 0) + goto fail; + pci_dev = s->devices[(s->config_reg >> 8) & 0xff]; + if (!pci_dev) { + fail: + switch(len) { + case 1: + val = 0xff; + break; + case 2: + val = 0xffff; + break; + default: + case 4: + val = 0xffffffff; + break; + } + goto the_end; + } + config_addr = (s->config_reg & 0xfc) | (addr & 3); + val = pci_dev->config_read(pci_dev, config_addr, len); +#if defined(DEBUG_PCI) + printf("pci_config_read: %s: addr=%02x val=%08x len=%d\n", + pci_dev->name, config_addr, val, len); +#endif + the_end: +#if defined(DEBUG_PCI) && 0 + printf("pci_data_read: addr=%08x val=%08x len=%d\n", + s->config_reg, val, len); +#endif + return val; +} + +static void pci_data_writeb(void* opaque, uint32_t addr, uint32_t val) +{ + pci_data_write(opaque, addr, val, 1); +} + +static void pci_data_writew(void* opaque, uint32_t addr, uint32_t val) +{ + pci_data_write(opaque, addr, val, 2); +} + +static void pci_data_writel(void* opaque, uint32_t addr, uint32_t val) +{ + pci_data_write(opaque, addr, val, 4); +} + +static uint32_t pci_data_readb(void* opaque, uint32_t addr) +{ + return pci_data_read(opaque, addr, 1); +} + +static uint32_t pci_data_readw(void* opaque, uint32_t addr) +{ + return pci_data_read(opaque, addr, 2); +} + +static uint32_t pci_data_readl(void* opaque, uint32_t addr) +{ + return pci_data_read(opaque, addr, 4); +} + +/* i440FX PCI bridge */ + +static void piix3_set_irq(PCIDevice *pci_dev, int irq_num, int level); + +PCIBus *i440fx_init(void) +{ + PCIBus *s; + PCIDevice *d; + + s = pci_register_bus(); + s->set_irq = piix3_set_irq; + + register_ioport_write(0xcf8, 4, 4, pci_addr_writel, s); + register_ioport_read(0xcf8, 4, 4, pci_addr_readl, s); + + register_ioport_write(0xcfc, 4, 1, pci_data_writeb, s); + register_ioport_write(0xcfc, 4, 2, pci_data_writew, s); + register_ioport_write(0xcfc, 4, 4, pci_data_writel, s); + register_ioport_read(0xcfc, 4, 1, pci_data_readb, s); + register_ioport_read(0xcfc, 4, 2, pci_data_readw, s); + register_ioport_read(0xcfc, 4, 4, pci_data_readl, s); + + d = pci_register_device(s, "i440FX", sizeof(PCIDevice), 0, + NULL, NULL); + + d->config[0x00] = 0x86; // vendor_id + d->config[0x01] = 0x80; + d->config[0x02] = 0x37; // device_id + d->config[0x03] = 0x12; + d->config[0x08] = 0x02; // revision + d->config[0x0a] = 0x00; // class_sub = host2pci + d->config[0x0b] = 0x06; // class_base = PCI_bridge + d->config[0x0e] = 0x00; // header_type + return s; +} + +/* PIIX3 PCI to ISA bridge */ + +typedef struct PIIX3State { + PCIDevice dev; +} PIIX3State; + +PIIX3State *piix3_state; + +/* return the global irq number corresponding to a given device irq + pin. We could also use the bus number to have a more precise + mapping. */ +static inline int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) +{ + int slot_addend; + slot_addend = (pci_dev->devfn >> 3) - 1; + return (irq_num + slot_addend) & 3; +} + +static inline int get_pci_irq_level(int irq_num) +{ + int pic_level; +#if (PCI_IRQ_WORDS == 2) + pic_level = ((pci_irq_levels[irq_num][0] | + pci_irq_levels[irq_num][1]) != 0); +#else + { + int i; + pic_level = 0; + for(i = 0; i < PCI_IRQ_WORDS; i++) { + if (pci_irq_levels[irq_num][i]) { + pic_level = 1; + break; + } + } + } +#endif + return pic_level; +} + +static void piix3_set_irq(PCIDevice *pci_dev, int irq_num, int level) +{ + int irq_index, shift, pic_irq, pic_level; + uint32_t *p; + + irq_num = pci_slot_get_pirq(pci_dev, irq_num); + irq_index = pci_dev->irq_index; + p = &pci_irq_levels[irq_num][irq_index >> 5]; + shift = (irq_index & 0x1f); + *p = (*p & ~(1 << shift)) | (level << shift); + + /* now we change the pic irq level according to the piix irq mappings */ + /* XXX: optimize */ + pic_irq = piix3_state->dev.config[0x60 + irq_num]; + if (pic_irq < 16) { + /* the pic level is the logical OR of all the PCI irqs mapped + to it */ + pic_level = 0; + if (pic_irq == piix3_state->dev.config[0x60]) + pic_level |= get_pci_irq_level(0); + if (pic_irq == piix3_state->dev.config[0x61]) + pic_level |= get_pci_irq_level(1); + if (pic_irq == piix3_state->dev.config[0x62]) + pic_level |= get_pci_irq_level(2); + if (pic_irq == piix3_state->dev.config[0x63]) + pic_level |= get_pci_irq_level(3); + pic_set_irq(pic_irq, pic_level); + } +} + +static void piix3_reset(PIIX3State *d) +{ + uint8_t *pci_conf = d->dev.config; + + pci_conf[0x04] = 0x07; // master, memory and I/O + pci_conf[0x05] = 0x00; + pci_conf[0x06] = 0x00; + pci_conf[0x07] = 0x02; // PCI_status_devsel_medium + pci_conf[0x4c] = 0x4d; + pci_conf[0x4e] = 0x03; + pci_conf[0x4f] = 0x00; + pci_conf[0x60] = 0x80; + pci_conf[0x69] = 0x02; + pci_conf[0x70] = 0x80; + pci_conf[0x76] = 0x0c; + pci_conf[0x77] = 0x0c; + pci_conf[0x78] = 0x02; + pci_conf[0x79] = 0x00; + pci_conf[0x80] = 0x00; + pci_conf[0x82] = 0x00; + pci_conf[0xa0] = 0x08; + pci_conf[0xa0] = 0x08; + pci_conf[0xa2] = 0x00; + pci_conf[0xa3] = 0x00; + pci_conf[0xa4] = 0x00; + pci_conf[0xa5] = 0x00; + pci_conf[0xa6] = 0x00; + pci_conf[0xa7] = 0x00; + pci_conf[0xa8] = 0x0f; + pci_conf[0xaa] = 0x00; + pci_conf[0xab] = 0x00; + pci_conf[0xac] = 0x00; + pci_conf[0xae] = 0x00; +} + +void piix3_init(PCIBus *bus) +{ + PIIX3State *d; + uint8_t *pci_conf; + + d = (PIIX3State *)pci_register_device(bus, "PIIX3", sizeof(PIIX3State), + -1, NULL, NULL); + register_savevm("PIIX3", 0, 1, generic_pci_save, generic_pci_load, d); + + piix3_state = d; + pci_conf = d->dev.config; + + pci_conf[0x00] = 0x86; // Intel + pci_conf[0x01] = 0x80; + pci_conf[0x02] = 0x00; // 82371SB PIIX3 PCI-to-ISA bridge (Step A1) + pci_conf[0x03] = 0x70; + pci_conf[0x0a] = 0x01; // class_sub = PCI_ISA + pci_conf[0x0b] = 0x06; // class_base = PCI_bridge + pci_conf[0x0e] = 0x80; // header_type = PCI_multifunction, generic + + piix3_reset(d); +} + +/* PREP pci init */ + +static inline void set_config(PCIBus *s, target_phys_addr_t addr) +{ + int devfn, i; + + for(i = 0; i < 11; i++) { + if ((addr & (1 << (11 + i))) != 0) + break; + } + devfn = ((addr >> 8) & 7) | (i << 3); + s->config_reg = 0x80000000 | (addr & 0xfc) | (devfn << 8); +} + +static void PPC_PCIIO_writeb (void *opaque, target_phys_addr_t addr, uint32_t val) +{ + PCIBus *s = opaque; + set_config(s, addr); + pci_data_write(s, addr, val, 1); +} + +static void PPC_PCIIO_writew (void *opaque, target_phys_addr_t addr, uint32_t val) +{ + PCIBus *s = opaque; + set_config(s, addr); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap16(val); +#endif + pci_data_write(s, addr, val, 2); +} + +static void PPC_PCIIO_writel (void *opaque, target_phys_addr_t addr, uint32_t val) +{ + PCIBus *s = opaque; + set_config(s, addr); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + pci_data_write(s, addr, val, 4); +} + +static uint32_t PPC_PCIIO_readb (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + set_config(s, addr); + val = pci_data_read(s, addr, 1); + return val; +} + +static uint32_t PPC_PCIIO_readw (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + set_config(s, addr); + val = pci_data_read(s, addr, 2); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap16(val); +#endif + return val; +} + +static uint32_t PPC_PCIIO_readl (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + set_config(s, addr); + val = pci_data_read(s, addr, 4); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + return val; +} + +static CPUWriteMemoryFunc *PPC_PCIIO_write[] = { + &PPC_PCIIO_writeb, + &PPC_PCIIO_writew, + &PPC_PCIIO_writel, +}; + +static CPUReadMemoryFunc *PPC_PCIIO_read[] = { + &PPC_PCIIO_readb, + &PPC_PCIIO_readw, + &PPC_PCIIO_readl, +}; + +static void prep_set_irq(PCIDevice *d, int irq_num, int level) +{ + /* XXX: we do not simulate the hardware - we rely on the BIOS to + set correctly for irq line field */ + pic_set_irq(d->config[PCI_INTERRUPT_LINE], level); +} + +PCIBus *pci_prep_init(void) +{ + PCIBus *s; + PCIDevice *d; + int PPC_io_memory; + + s = pci_register_bus(); + s->set_irq = prep_set_irq; + + register_ioport_write(0xcf8, 4, 4, pci_addr_writel, s); + register_ioport_read(0xcf8, 4, 4, pci_addr_readl, s); + + register_ioport_write(0xcfc, 4, 1, pci_data_writeb, s); + register_ioport_write(0xcfc, 4, 2, pci_data_writew, s); + register_ioport_write(0xcfc, 4, 4, pci_data_writel, s); + register_ioport_read(0xcfc, 4, 1, pci_data_readb, s); + register_ioport_read(0xcfc, 4, 2, pci_data_readw, s); + register_ioport_read(0xcfc, 4, 4, pci_data_readl, s); + + PPC_io_memory = cpu_register_io_memory(0, PPC_PCIIO_read, + PPC_PCIIO_write, s); + cpu_register_physical_memory(0x80800000, 0x00400000, PPC_io_memory); + + /* PCI host bridge */ + d = pci_register_device(s, "PREP Host Bridge - Motorola Raven", + sizeof(PCIDevice), 0, NULL, NULL); + d->config[0x00] = 0x57; // vendor_id : Motorola + d->config[0x01] = 0x10; + d->config[0x02] = 0x01; // device_id : Raven + d->config[0x03] = 0x48; + d->config[0x08] = 0x00; // revision + d->config[0x0A] = 0x00; // class_sub = pci host + d->config[0x0B] = 0x06; // class_base = PCI_bridge + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x10; // latency_timer + d->config[0x0E] = 0x00; // header_type + d->config[0x34] = 0x00; // capabilities_pointer + + return s; +} + + +/* Grackle PCI host */ +static void pci_grackle_config_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + s->config_reg = val; +} + +static uint32_t pci_grackle_config_readl (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = s->config_reg; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + return val; +} + +static CPUWriteMemoryFunc *pci_grackle_config_write[] = { + &pci_grackle_config_writel, + &pci_grackle_config_writel, + &pci_grackle_config_writel, +}; + +static CPUReadMemoryFunc *pci_grackle_config_read[] = { + &pci_grackle_config_readl, + &pci_grackle_config_readl, + &pci_grackle_config_readl, +}; + +static void pci_grackle_writeb (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + pci_data_write(s, addr, val, 1); +} + +static void pci_grackle_writew (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap16(val); +#endif + pci_data_write(s, addr, val, 2); +} + +static void pci_grackle_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + pci_data_write(s, addr, val, 4); +} + +static uint32_t pci_grackle_readb (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + val = pci_data_read(s, addr, 1); + return val; +} + +static uint32_t pci_grackle_readw (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + val = pci_data_read(s, addr, 2); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap16(val); +#endif + return val; +} + +static uint32_t pci_grackle_readl (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr, 4); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + return val; +} + +static CPUWriteMemoryFunc *pci_grackle_write[] = { + &pci_grackle_writeb, + &pci_grackle_writew, + &pci_grackle_writel, +}; + +static CPUReadMemoryFunc *pci_grackle_read[] = { + &pci_grackle_readb, + &pci_grackle_readw, + &pci_grackle_readl, +}; + +void pci_set_pic(PCIBus *bus, SetIRQFunc *set_irq, void *irq_opaque) +{ + bus->low_set_irq = set_irq; + bus->irq_opaque = irq_opaque; +} + +/* XXX: we do not simulate the hardware - we rely on the BIOS to + set correctly for irq line field */ +static void pci_set_irq_simple(PCIDevice *d, int irq_num, int level) +{ + PCIBus *s = d->bus; + s->low_set_irq(s->irq_opaque, d->config[PCI_INTERRUPT_LINE], level); +} + +PCIBus *pci_grackle_init(uint32_t base) +{ + PCIBus *s; + PCIDevice *d; + int pci_mem_config, pci_mem_data; + + s = pci_register_bus(); + s->set_irq = pci_set_irq_simple; + + pci_mem_config = cpu_register_io_memory(0, pci_grackle_config_read, + pci_grackle_config_write, s); + pci_mem_data = cpu_register_io_memory(0, pci_grackle_read, + pci_grackle_write, s); + cpu_register_physical_memory(base, 0x1000, pci_mem_config); + cpu_register_physical_memory(base + 0x00200000, 0x1000, pci_mem_data); + d = pci_register_device(s, "Grackle host bridge", sizeof(PCIDevice), + 0, NULL, NULL); + d->config[0x00] = 0x57; // vendor_id + d->config[0x01] = 0x10; + d->config[0x02] = 0x02; // device_id + d->config[0x03] = 0x00; + d->config[0x08] = 0x00; // revision + d->config[0x09] = 0x01; + d->config[0x0a] = 0x00; // class_sub = host + d->config[0x0b] = 0x06; // class_base = PCI_bridge + d->config[0x0e] = 0x00; // header_type + + d->config[0x18] = 0x00; // primary_bus + d->config[0x19] = 0x01; // secondary_bus + d->config[0x1a] = 0x00; // subordinate_bus + d->config[0x1c] = 0x00; + d->config[0x1d] = 0x00; + + d->config[0x20] = 0x00; // memory_base + d->config[0x21] = 0x00; + d->config[0x22] = 0x01; // memory_limit + d->config[0x23] = 0x00; + + d->config[0x24] = 0x00; // prefetchable_memory_base + d->config[0x25] = 0x00; + d->config[0x26] = 0x00; // prefetchable_memory_limit + d->config[0x27] = 0x00; + +#if 0 + /* PCI2PCI bridge same values as PearPC - check this */ + d->config[0x00] = 0x11; // vendor_id + d->config[0x01] = 0x10; + d->config[0x02] = 0x26; // device_id + d->config[0x03] = 0x00; + d->config[0x08] = 0x02; // revision + d->config[0x0a] = 0x04; // class_sub = pci2pci + d->config[0x0b] = 0x06; // class_base = PCI_bridge + d->config[0x0e] = 0x01; // header_type + + d->config[0x18] = 0x0; // primary_bus + d->config[0x19] = 0x1; // secondary_bus + d->config[0x1a] = 0x1; // subordinate_bus + d->config[0x1c] = 0x10; // io_base + d->config[0x1d] = 0x20; // io_limit + + d->config[0x20] = 0x80; // memory_base + d->config[0x21] = 0x80; + d->config[0x22] = 0x90; // memory_limit + d->config[0x23] = 0x80; + + d->config[0x24] = 0x00; // prefetchable_memory_base + d->config[0x25] = 0x84; + d->config[0x26] = 0x00; // prefetchable_memory_limit + d->config[0x27] = 0x85; +#endif + return s; +} + +/* Uninorth PCI host (for all Mac99 and newer machines */ +static void pci_unin_main_config_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + int i; + +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + + for (i = 11; i < 32; i++) { + if ((val & (1 << i)) != 0) + break; + } +#if 0 + s->config_reg = 0x80000000 | (1 << 16) | (val & 0x7FC) | (i << 11); +#else + s->config_reg = 0x80000000 | (0 << 16) | (val & 0x7FC) | (i << 11); +#endif +} + +static uint32_t pci_unin_main_config_readl (void *opaque, + target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + int devfn; + + devfn = (s->config_reg >> 8) & 0xFF; + val = (1 << (devfn >> 3)) | ((devfn & 0x07) << 8) | (s->config_reg & 0xFC); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + + return val; +} + +static CPUWriteMemoryFunc *pci_unin_main_config_write[] = { + &pci_unin_main_config_writel, + &pci_unin_main_config_writel, + &pci_unin_main_config_writel, +}; + +static CPUReadMemoryFunc *pci_unin_main_config_read[] = { + &pci_unin_main_config_readl, + &pci_unin_main_config_readl, + &pci_unin_main_config_readl, +}; + +static void pci_unin_main_writeb (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + pci_data_write(s, addr & 7, val, 1); +} + +static void pci_unin_main_writew (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap16(val); +#endif + pci_data_write(s, addr & 7, val, 2); +} + +static void pci_unin_main_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + pci_data_write(s, addr & 7, val, 4); +} + +static uint32_t pci_unin_main_readb (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr & 7, 1); + + return val; +} + +static uint32_t pci_unin_main_readw (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr & 7, 2); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap16(val); +#endif + + return val; +} + +static uint32_t pci_unin_main_readl (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr, 4); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + + return val; +} + +static CPUWriteMemoryFunc *pci_unin_main_write[] = { + &pci_unin_main_writeb, + &pci_unin_main_writew, + &pci_unin_main_writel, +}; + +static CPUReadMemoryFunc *pci_unin_main_read[] = { + &pci_unin_main_readb, + &pci_unin_main_readw, + &pci_unin_main_readl, +}; + +#if 0 + +static void pci_unin_config_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + s->config_reg = 0x80000000 | (val & ~0x00000001); +} + +static uint32_t pci_unin_config_readl (void *opaque, + target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = (s->config_reg | 0x00000001) & ~0x80000000; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + + return val; +} + +static CPUWriteMemoryFunc *pci_unin_config_write[] = { + &pci_unin_config_writel, + &pci_unin_config_writel, + &pci_unin_config_writel, +}; + +static CPUReadMemoryFunc *pci_unin_config_read[] = { + &pci_unin_config_readl, + &pci_unin_config_readl, + &pci_unin_config_readl, +}; + +static void pci_unin_writeb (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + pci_data_write(s, addr & 3, val, 1); +} + +static void pci_unin_writew (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap16(val); +#endif + pci_data_write(s, addr & 3, val, 2); +} + +static void pci_unin_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + pci_data_write(s, addr & 3, val, 4); +} + +static uint32_t pci_unin_readb (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr & 3, 1); + + return val; +} + +static uint32_t pci_unin_readw (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr & 3, 2); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap16(val); +#endif + + return val; +} + +static uint32_t pci_unin_readl (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr & 3, 4); +#ifdef TARGET_WORDS_BIGENDIAN + val = bswap32(val); +#endif + + return val; +} + +static CPUWriteMemoryFunc *pci_unin_write[] = { + &pci_unin_writeb, + &pci_unin_writew, + &pci_unin_writel, +}; + +static CPUReadMemoryFunc *pci_unin_read[] = { + &pci_unin_readb, + &pci_unin_readw, + &pci_unin_readl, +}; +#endif + +PCIBus *pci_pmac_init(void) +{ + PCIBus *s; + PCIDevice *d; + int pci_mem_config, pci_mem_data; + + /* Use values found on a real PowerMac */ + /* Uninorth main bus */ + s = pci_register_bus(); + s->set_irq = pci_set_irq_simple; + + pci_mem_config = cpu_register_io_memory(0, pci_unin_main_config_read, + pci_unin_main_config_write, s); + pci_mem_data = cpu_register_io_memory(0, pci_unin_main_read, + pci_unin_main_write, s); + cpu_register_physical_memory(0xf2800000, 0x1000, pci_mem_config); + cpu_register_physical_memory(0xf2c00000, 0x1000, pci_mem_data); + s->devfn_min = 11 << 3; + d = pci_register_device(s, "Uni-north main", sizeof(PCIDevice), + 11 << 3, NULL, NULL); + d->config[0x00] = 0x6b; // vendor_id : Apple + d->config[0x01] = 0x10; + d->config[0x02] = 0x1F; // device_id + d->config[0x03] = 0x00; + d->config[0x08] = 0x00; // revision + d->config[0x0A] = 0x00; // class_sub = pci host + d->config[0x0B] = 0x06; // class_base = PCI_bridge + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x10; // latency_timer + d->config[0x0E] = 0x00; // header_type + d->config[0x34] = 0x00; // capabilities_pointer + +#if 0 // XXX: not activated as PPC BIOS doesn't handle mutiple buses properly + /* pci-to-pci bridge */ + d = pci_register_device("Uni-north bridge", sizeof(PCIDevice), 0, 13 << 3, + NULL, NULL); + d->config[0x00] = 0x11; // vendor_id : TI + d->config[0x01] = 0x10; + d->config[0x02] = 0x26; // device_id + d->config[0x03] = 0x00; + d->config[0x08] = 0x05; // revision + d->config[0x0A] = 0x04; // class_sub = pci2pci + d->config[0x0B] = 0x06; // class_base = PCI_bridge + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x20; // latency_timer + d->config[0x0E] = 0x01; // header_type + + d->config[0x18] = 0x01; // primary_bus + d->config[0x19] = 0x02; // secondary_bus + d->config[0x1A] = 0x02; // subordinate_bus + d->config[0x1B] = 0x20; // secondary_latency_timer + d->config[0x1C] = 0x11; // io_base + d->config[0x1D] = 0x01; // io_limit + d->config[0x20] = 0x00; // memory_base + d->config[0x21] = 0x80; + d->config[0x22] = 0x00; // memory_limit + d->config[0x23] = 0x80; + d->config[0x24] = 0x01; // prefetchable_memory_base + d->config[0x25] = 0x80; + d->config[0x26] = 0xF1; // prefectchable_memory_limit + d->config[0x27] = 0x7F; + // d->config[0x34] = 0xdc // capabilities_pointer +#endif +#if 0 // XXX: not needed for now + /* Uninorth AGP bus */ + s = &pci_bridge[1]; + pci_mem_config = cpu_register_io_memory(0, pci_unin_config_read, + pci_unin_config_write, s); + pci_mem_data = cpu_register_io_memory(0, pci_unin_read, + pci_unin_write, s); + cpu_register_physical_memory(0xf0800000, 0x1000, pci_mem_config); + cpu_register_physical_memory(0xf0c00000, 0x1000, pci_mem_data); + + d = pci_register_device("Uni-north AGP", sizeof(PCIDevice), 0, 11 << 3, + NULL, NULL); + d->config[0x00] = 0x6b; // vendor_id : Apple + d->config[0x01] = 0x10; + d->config[0x02] = 0x20; // device_id + d->config[0x03] = 0x00; + d->config[0x08] = 0x00; // revision + d->config[0x0A] = 0x00; // class_sub = pci host + d->config[0x0B] = 0x06; // class_base = PCI_bridge + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x10; // latency_timer + d->config[0x0E] = 0x00; // header_type + // d->config[0x34] = 0x80; // capabilities_pointer +#endif + +#if 0 // XXX: not needed for now + /* Uninorth internal bus */ + s = &pci_bridge[2]; + pci_mem_config = cpu_register_io_memory(0, pci_unin_config_read, + pci_unin_config_write, s); + pci_mem_data = cpu_register_io_memory(0, pci_unin_read, + pci_unin_write, s); + cpu_register_physical_memory(0xf4800000, 0x1000, pci_mem_config); + cpu_register_physical_memory(0xf4c00000, 0x1000, pci_mem_data); + + d = pci_register_device("Uni-north internal", sizeof(PCIDevice), + 3, 11 << 3, NULL, NULL); + d->config[0x00] = 0x6b; // vendor_id : Apple + d->config[0x01] = 0x10; + d->config[0x02] = 0x1E; // device_id + d->config[0x03] = 0x00; + d->config[0x08] = 0x00; // revision + d->config[0x0A] = 0x00; // class_sub = pci host + d->config[0x0B] = 0x06; // class_base = PCI_bridge + d->config[0x0C] = 0x08; // cache_line_size + d->config[0x0D] = 0x10; // latency_timer + d->config[0x0E] = 0x00; // header_type + d->config[0x34] = 0x00; // capabilities_pointer +#endif + return s; +} + +/* Ultrasparc APB PCI host */ +static void pci_apb_config_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + int i; + + for (i = 11; i < 32; i++) { + if ((val & (1 << i)) != 0) + break; + } + s->config_reg = 0x80000000 | (1 << 16) | (val & 0x7FC) | (i << 11); +} + +static uint32_t pci_apb_config_readl (void *opaque, + target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + int devfn; + + devfn = (s->config_reg >> 8) & 0xFF; + val = (1 << (devfn >> 3)) | ((devfn & 0x07) << 8) | (s->config_reg & 0xFC); + return val; +} + +static CPUWriteMemoryFunc *pci_apb_config_write[] = { + &pci_apb_config_writel, + &pci_apb_config_writel, + &pci_apb_config_writel, +}; + +static CPUReadMemoryFunc *pci_apb_config_read[] = { + &pci_apb_config_readl, + &pci_apb_config_readl, + &pci_apb_config_readl, +}; + +static void apb_config_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + //PCIBus *s = opaque; + + switch (addr & 0x3f) { + case 0x00: // Control/Status + case 0x10: // AFSR + case 0x18: // AFAR + case 0x20: // Diagnostic + case 0x28: // Target address space + // XXX + default: + break; + } +} + +static uint32_t apb_config_readl (void *opaque, + target_phys_addr_t addr) +{ + //PCIBus *s = opaque; + uint32_t val; + + switch (addr & 0x3f) { + case 0x00: // Control/Status + case 0x10: // AFSR + case 0x18: // AFAR + case 0x20: // Diagnostic + case 0x28: // Target address space + // XXX + default: + val = 0; + break; + } + return val; +} + +static CPUWriteMemoryFunc *apb_config_write[] = { + &apb_config_writel, + &apb_config_writel, + &apb_config_writel, +}; + +static CPUReadMemoryFunc *apb_config_read[] = { + &apb_config_readl, + &apb_config_readl, + &apb_config_readl, +}; + +static void pci_apb_writeb (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + + pci_data_write(s, addr & 7, val, 1); +} + +static void pci_apb_writew (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + + pci_data_write(s, addr & 7, val, 2); +} + +static void pci_apb_writel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBus *s = opaque; + + pci_data_write(s, addr & 7, val, 4); +} + +static uint32_t pci_apb_readb (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr & 7, 1); + return val; +} + +static uint32_t pci_apb_readw (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr & 7, 2); + return val; +} + +static uint32_t pci_apb_readl (void *opaque, target_phys_addr_t addr) +{ + PCIBus *s = opaque; + uint32_t val; + + val = pci_data_read(s, addr, 4); + return val; +} + +static CPUWriteMemoryFunc *pci_apb_write[] = { + &pci_apb_writeb, + &pci_apb_writew, + &pci_apb_writel, +}; + +static CPUReadMemoryFunc *pci_apb_read[] = { + &pci_apb_readb, + &pci_apb_readw, + &pci_apb_readl, +}; + +static void pci_apb_iowriteb (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + cpu_outb(NULL, addr & 0xffff, val); +} + +static void pci_apb_iowritew (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + cpu_outw(NULL, addr & 0xffff, val); +} + +static void pci_apb_iowritel (void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + cpu_outl(NULL, addr & 0xffff, val); +} + +static uint32_t pci_apb_ioreadb (void *opaque, target_phys_addr_t addr) +{ + uint32_t val; + + val = cpu_inb(NULL, addr & 0xffff); + return val; +} + +static uint32_t pci_apb_ioreadw (void *opaque, target_phys_addr_t addr) +{ + uint32_t val; + + val = cpu_inw(NULL, addr & 0xffff); + return val; +} + +static uint32_t pci_apb_ioreadl (void *opaque, target_phys_addr_t addr) +{ + uint32_t val; + + val = cpu_inl(NULL, addr & 0xffff); + return val; +} + +static CPUWriteMemoryFunc *pci_apb_iowrite[] = { + &pci_apb_iowriteb, + &pci_apb_iowritew, + &pci_apb_iowritel, +}; + +static CPUReadMemoryFunc *pci_apb_ioread[] = { + &pci_apb_ioreadb, + &pci_apb_ioreadw, + &pci_apb_ioreadl, +}; + +PCIBus *pci_apb_init(target_ulong special_base, target_ulong mem_base) +{ + PCIBus *s; + PCIDevice *d; + int pci_mem_config, pci_mem_data, apb_config, pci_ioport; + + /* Ultrasparc APB main bus */ + s = pci_register_bus(); + s->set_irq = pci_set_irq_simple; + + pci_mem_config = cpu_register_io_memory(0, pci_apb_config_read, + pci_apb_config_write, s); + apb_config = cpu_register_io_memory(0, apb_config_read, + apb_config_write, s); + pci_mem_data = cpu_register_io_memory(0, pci_apb_read, + pci_apb_write, s); + pci_ioport = cpu_register_io_memory(0, pci_apb_ioread, + pci_apb_iowrite, s); + + cpu_register_physical_memory(special_base + 0x2000ULL, 0x40, apb_config); + cpu_register_physical_memory(special_base + 0x1000000ULL, 0x10, pci_mem_config); + cpu_register_physical_memory(special_base + 0x2000000ULL, 0x10000, pci_ioport); + cpu_register_physical_memory(mem_base, 0x10000000, pci_mem_data); // XXX size should be 4G-prom + + d = pci_register_device(s, "Advanced PCI Bus", sizeof(PCIDevice), + -1, NULL, NULL); + d->config[0x00] = 0x8e; // vendor_id : Sun + d->config[0x01] = 0x10; + d->config[0x02] = 0x00; // device_id + d->config[0x03] = 0xa0; + d->config[0x04] = 0x06; // command = bus master, pci mem + d->config[0x05] = 0x00; + d->config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error + d->config[0x07] = 0x03; // status = medium devsel + d->config[0x08] = 0x00; // revision + d->config[0x09] = 0x00; // programming i/f + d->config[0x0A] = 0x00; // class_sub = pci host + d->config[0x0B] = 0x06; // class_base = PCI_bridge + d->config[0x0D] = 0x10; // latency_timer + d->config[0x0E] = 0x00; // header_type + return s; +} + +/***********************************************************/ +/* generic PCI irq support */ + +/* 0 <= irq_num <= 3. level must be 0 or 1 */ +void pci_set_irq(PCIDevice *pci_dev, int irq_num, int level) +{ + PCIBus *bus = pci_dev->bus; + bus->set_irq(pci_dev, irq_num, level); +} + +/***********************************************************/ +/* monitor info on PCI */ + +static void pci_info_device(PCIDevice *d) +{ + int i, class; + PCIIORegion *r; + + term_printf(" Bus %2d, device %3d, function %d:\n", + d->bus->bus_num, d->devfn >> 3, d->devfn & 7); + class = le16_to_cpu(*((uint16_t *)(d->config + PCI_CLASS_DEVICE))); + term_printf(" "); + switch(class) { + case 0x0101: + term_printf("IDE controller"); + break; + case 0x0200: + term_printf("Ethernet controller"); + break; + case 0x0300: + term_printf("VGA controller"); + break; + default: + term_printf("Class %04x", class); + break; + } + term_printf(": PCI device %04x:%04x\n", + le16_to_cpu(*((uint16_t *)(d->config + PCI_VENDOR_ID))), + le16_to_cpu(*((uint16_t *)(d->config + PCI_DEVICE_ID)))); + + if (d->config[PCI_INTERRUPT_PIN] != 0) { + term_printf(" IRQ %d.\n", d->config[PCI_INTERRUPT_LINE]); + } + for(i = 0;i < PCI_NUM_REGIONS; i++) { + r = &d->io_regions[i]; + if (r->size != 0) { + term_printf(" BAR%d: ", i); + if (r->type & PCI_ADDRESS_SPACE_IO) { + term_printf("I/O at 0x%04x [0x%04x].\n", + r->addr, r->addr + r->size - 1); + } else { + term_printf("32 bit memory at 0x%08x [0x%08x].\n", + r->addr, r->addr + r->size - 1); + } + } + } +} + +void pci_info(void) +{ + PCIBus *bus = first_bus; + PCIDevice *d; + int devfn; + + if (bus) { + for(devfn = 0; devfn < 256; devfn++) { + d = bus->devices[devfn]; + if (d) + pci_info_device(d); + } + } +} + +/***********************************************************/ +/* XXX: the following should be moved to the PC BIOS */ + +static __attribute__((unused)) uint32_t isa_inb(uint32_t addr) +{ + return cpu_inb(NULL, addr); +} + +static void isa_outb(uint32_t val, uint32_t addr) +{ + cpu_outb(NULL, addr, val); +} + +static __attribute__((unused)) uint32_t isa_inw(uint32_t addr) +{ + return cpu_inw(NULL, addr); +} + +static __attribute__((unused)) void isa_outw(uint32_t val, uint32_t addr) +{ + cpu_outw(NULL, addr, val); +} + +static __attribute__((unused)) uint32_t isa_inl(uint32_t addr) +{ + return cpu_inl(NULL, addr); +} + +static __attribute__((unused)) void isa_outl(uint32_t val, uint32_t addr) +{ + cpu_outl(NULL, addr, val); +} + +static void pci_config_writel(PCIDevice *d, uint32_t addr, uint32_t val) +{ + PCIBus *s = d->bus; + s->config_reg = 0x80000000 | (s->bus_num << 16) | + (d->devfn << 8) | addr; + pci_data_write(s, 0, val, 4); +} + +static void pci_config_writew(PCIDevice *d, uint32_t addr, uint32_t val) +{ + PCIBus *s = d->bus; + s->config_reg = 0x80000000 | (s->bus_num << 16) | + (d->devfn << 8) | (addr & ~3); + pci_data_write(s, addr & 3, val, 2); +} + +static void pci_config_writeb(PCIDevice *d, uint32_t addr, uint32_t val) +{ + PCIBus *s = d->bus; + s->config_reg = 0x80000000 | (s->bus_num << 16) | + (d->devfn << 8) | (addr & ~3); + pci_data_write(s, addr & 3, val, 1); +} + +static __attribute__((unused)) uint32_t pci_config_readl(PCIDevice *d, uint32_t addr) +{ + PCIBus *s = d->bus; + s->config_reg = 0x80000000 | (s->bus_num << 16) | + (d->devfn << 8) | addr; + return pci_data_read(s, 0, 4); +} + +static uint32_t pci_config_readw(PCIDevice *d, uint32_t addr) +{ + PCIBus *s = d->bus; + s->config_reg = 0x80000000 | (s->bus_num << 16) | + (d->devfn << 8) | (addr & ~3); + return pci_data_read(s, addr & 3, 2); +} + +static uint32_t pci_config_readb(PCIDevice *d, uint32_t addr) +{ + PCIBus *s = d->bus; + s->config_reg = 0x80000000 | (s->bus_num << 16) | + (d->devfn << 8) | (addr & ~3); + return pci_data_read(s, addr & 3, 1); +} + +static uint32_t pci_bios_io_addr; +static uint32_t pci_bios_mem_addr; +/* host irqs corresponding to PCI irqs A-D */ +static uint8_t pci_irqs[4] = { 10, 11, 10, 11 }; + +static void pci_set_io_region_addr(PCIDevice *d, int region_num, uint32_t addr) +{ + PCIIORegion *r; + uint16_t cmd; + uint32_t ofs; + + if ( region_num == PCI_ROM_SLOT ) { + ofs = 0x30; + }else{ + ofs = 0x10 + region_num * 4; + } + + pci_config_writel(d, ofs, addr); + r = &d->io_regions[region_num]; + + /* enable memory mappings */ + cmd = pci_config_readw(d, PCI_COMMAND); + if ( region_num == PCI_ROM_SLOT ) + cmd |= 2; + else if (r->type & PCI_ADDRESS_SPACE_IO) + cmd |= 1; + else + cmd |= 2; + pci_config_writew(d, PCI_COMMAND, cmd); +} + +static void pci_bios_init_device(PCIDevice *d) +{ + int class; + PCIIORegion *r; + uint32_t *paddr; + int i, pin, pic_irq, vendor_id, device_id; + + class = pci_config_readw(d, PCI_CLASS_DEVICE); + vendor_id = pci_config_readw(d, PCI_VENDOR_ID); + device_id = pci_config_readw(d, PCI_DEVICE_ID); + switch(class) { + case 0x0101: + if (vendor_id == 0x8086 && device_id == 0x7010) { + /* PIIX3 IDE */ + pci_config_writew(d, 0x40, 0x8000); // enable IDE0 + pci_config_writew(d, 0x42, 0x8000); // enable IDE1 + goto default_map; + } else { + /* IDE: we map it as in ISA mode */ + pci_set_io_region_addr(d, 0, 0x1f0); + pci_set_io_region_addr(d, 1, 0x3f4); + pci_set_io_region_addr(d, 2, 0x170); + pci_set_io_region_addr(d, 3, 0x374); + } + break; + case 0x0680: + if (vendor_id == 0x8086 && device_id == 0x7113) { + // PIIX4 ACPI PM + pci_config_writew(d, 0x20, 0x0000); // NO smb bus IO enable in PIIX4 + pci_config_writew(d, 0x22, 0x0000); + goto default_map; + } + break; + + case 0x0300: + if (vendor_id != 0x1234) + goto default_map; + /* VGA: map frame buffer to default Bochs VBE address */ + pci_set_io_region_addr(d, 0, 0xE0000000); + break; + + case 0x0800: + /* PIC */ + vendor_id = pci_config_readw(d, PCI_VENDOR_ID); + device_id = pci_config_readw(d, PCI_DEVICE_ID); + if (vendor_id == 0x1014) { + /* IBM */ + if (device_id == 0x0046 || device_id == 0xFFFF) { + /* MPIC & MPIC2 */ + pci_set_io_region_addr(d, 0, 0x80800000 + 0x00040000); + } + } + break; + case 0xff00: + if (vendor_id == 0x0106b && + (device_id == 0x0017 || device_id == 0x0022)) { + /* macio bridge */ + pci_set_io_region_addr(d, 0, 0x80800000); + } + break; + default: + default_map: + /* default memory mappings */ + for(i = 0; i < PCI_NUM_REGIONS; i++) { + r = &d->io_regions[i]; + if (r->size) { + if (r->type & PCI_ADDRESS_SPACE_IO) + paddr = &pci_bios_io_addr; + else + paddr = &pci_bios_mem_addr; + *paddr = (*paddr + r->size - 1) & ~(r->size - 1); + pci_set_io_region_addr(d, i, *paddr); + *paddr += r->size; + } + } + break; + } + + /* map the interrupt */ + pin = pci_config_readb(d, PCI_INTERRUPT_PIN); + if (pin != 0) { + pin = pci_slot_get_pirq(d, pin - 1); + pic_irq = pci_irqs[pin]; + pci_config_writeb(d, PCI_INTERRUPT_LINE, pic_irq); + } + if (class== 0x0680&& vendor_id == 0x8086 && device_id == 0x7113) { + // PIIX4 ACPI PM + pci_config_writew(d, 0x20, 0x0000); // NO smb bus IO enable in PIIX4 + pci_config_writew(d, 0x22, 0x0000); + pci_config_writew(d, 0x3c, 0x0009); // Hardcodeed IRQ9 + pci_config_writew(d, 0x3d, 0x0001); + } +} + +/* + * This function initializes the PCI devices as a normal PCI BIOS + * would do. It is provided just in case the BIOS has no support for + * PCI. + */ +void pci_bios_init(void) +{ + PCIBus *bus; + PCIDevice *d; + int devfn, i, irq; + uint8_t elcr[2]; + + pci_bios_io_addr = 0xc000; + pci_bios_mem_addr = 0xf0000000; + + /* activate IRQ mappings */ + elcr[0] = 0x00; + elcr[1] = 0x00; + for(i = 0; i < 4; i++) { + irq = pci_irqs[i]; + /* set to trigger level */ + elcr[irq >> 3] |= (1 << (irq & 7)); + /* activate irq remapping in PIIX */ + pci_config_writeb((PCIDevice *)piix3_state, 0x60 + i, irq); + } + isa_outb(elcr[0], 0x4d0); + isa_outb(elcr[1], 0x4d1); + + bus = first_bus; + if (bus) { + for(devfn = 0; devfn < 256; devfn++) { + d = bus->devices[devfn]; + if (d) + pci_bios_init_device(d); + } + } +} + +/* Initialize a PCI NIC. */ +void pci_nic_init(PCIBus *bus, NICInfo *nd) +{ + if (strcmp(nd->model, "ne2k_pci") == 0) { + pci_ne2000_init(bus, nd); + } else if (strcmp(nd->model, "rtl8139") == 0) { + pci_rtl8139_init(bus, nd); + } else { + fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model); + exit (1); + } +} + diff --git a/tools/ioemu/hw/pckbd.c b/tools/ioemu/hw/pckbd.c new file mode 100644 index 0000000000..3c41e5f602 --- /dev/null +++ b/tools/ioemu/hw/pckbd.c @@ -0,0 +1,370 @@ +/* + * QEMU PC keyboard emulation + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* debug PC keyboard */ +//#define DEBUG_KBD + +/* debug PC keyboard : only mouse */ +//#define DEBUG_MOUSE + +/* Keyboard Controller Commands */ +#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */ +#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */ +#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */ +#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */ +#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */ +#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */ +#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */ +#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */ +#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */ +#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */ +#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */ +#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */ +#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */ +#define KBD_CCMD_WRITE_OBUF 0xD2 +#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if + initiated by the auxiliary device */ +#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */ +#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */ +#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */ +#define KBD_CCMD_RESET 0xFE + +/* Keyboard Commands */ +#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ +#define KBD_CMD_ECHO 0xEE +#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */ +#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ +#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ +#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */ +#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */ +#define KBD_CMD_RESET 0xFF /* Reset */ + +/* Keyboard Replies */ +#define KBD_REPLY_POR 0xAA /* Power on reset */ +#define KBD_REPLY_ACK 0xFA /* Command ACK */ +#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ + +/* Status Register Bits */ +#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */ +#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ +#define KBD_STAT_SELFTEST 0x04 /* Self test successful */ +#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */ +#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */ +#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */ +#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */ +#define KBD_STAT_PERR 0x80 /* Parity error */ + +/* Controller Mode Register Bits */ +#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */ +#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */ +#define KBD_MODE_SYS 0x04 /* The system flag (?) */ +#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */ +#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */ +#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */ +#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */ +#define KBD_MODE_RFU 0x80 + +/* Mouse Commands */ +#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ +#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ +#define AUX_SET_RES 0xE8 /* Set resolution */ +#define AUX_GET_SCALE 0xE9 /* Get scaling factor */ +#define AUX_SET_STREAM 0xEA /* Set stream mode */ +#define AUX_POLL 0xEB /* Poll */ +#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */ +#define AUX_SET_WRAP 0xEE /* Set wrap mode */ +#define AUX_SET_REMOTE 0xF0 /* Set remote mode */ +#define AUX_GET_TYPE 0xF2 /* Get type */ +#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ +#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ +#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ +#define AUX_SET_DEFAULT 0xF6 +#define AUX_RESET 0xFF /* Reset aux device */ +#define AUX_ACK 0xFA /* Command byte ACK. */ + +#define MOUSE_STATUS_REMOTE 0x40 +#define MOUSE_STATUS_ENABLED 0x20 +#define MOUSE_STATUS_SCALE21 0x10 + +#define KBD_QUEUE_SIZE 256 + +#define KBD_PENDING_KBD 1 +#define KBD_PENDING_AUX 2 + +typedef struct KBDState { + uint8_t write_cmd; /* if non zero, write data to port 60 is expected */ + uint8_t status; + uint8_t mode; + /* Bitmask of devices with data available. */ + uint8_t pending; + void *kbd; + void *mouse; +} KBDState; + +KBDState kbd_state; + +/* update irq and KBD_STAT_[MOUSE_]OBF */ +/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be + incorrect, but it avoids having to simulate exact delays */ +static void kbd_update_irq(KBDState *s) +{ + int irq12_level, irq1_level; + + irq1_level = 0; + irq12_level = 0; + s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF); + if (s->pending) { + s->status |= KBD_STAT_OBF; + /* kdb data takes priority over aux data. */ + if (s->pending == KBD_PENDING_AUX) { + s->status |= KBD_STAT_MOUSE_OBF; + if (s->mode & KBD_MODE_MOUSE_INT) + irq12_level = 1; + } else { + if ((s->mode & KBD_MODE_KBD_INT) && + !(s->mode & KBD_MODE_DISABLE_KBD)) + irq1_level = 1; + } + } + pic_set_irq(1, irq1_level); + pic_set_irq(12, irq12_level); +} + +static void kbd_update_kbd_irq(void *opaque, int level) +{ + KBDState *s = (KBDState *)opaque; + + if (level) + s->pending |= KBD_PENDING_KBD; + else + s->pending &= ~KBD_PENDING_KBD; + kbd_update_irq(s); +} + +static void kbd_update_aux_irq(void *opaque, int level) +{ + KBDState *s = (KBDState *)opaque; + + if (level) + s->pending |= KBD_PENDING_AUX; + else + s->pending &= ~KBD_PENDING_AUX; + kbd_update_irq(s); +} + +static uint32_t kbd_read_status(void *opaque, uint32_t addr) +{ + KBDState *s = opaque; + int val; + val = s->status; +#if defined(DEBUG_KBD) + printf("kbd: read status=0x%02x\n", val); +#endif + return val; +} + +static void kbd_queue(KBDState *s, int b, int aux) +{ + if (aux) + ps2_queue(s->mouse, b); + else + ps2_queue(s->kbd, b); +} + +static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val) +{ + KBDState *s = opaque; + +#ifdef DEBUG_KBD + printf("kbd: write cmd=0x%02x\n", val); +#endif + switch(val) { + case KBD_CCMD_READ_MODE: + kbd_queue(s, s->mode, 0); + break; + case KBD_CCMD_WRITE_MODE: + case KBD_CCMD_WRITE_OBUF: + case KBD_CCMD_WRITE_AUX_OBUF: + case KBD_CCMD_WRITE_MOUSE: + case KBD_CCMD_WRITE_OUTPORT: + s->write_cmd = val; + break; + case KBD_CCMD_MOUSE_DISABLE: + s->mode |= KBD_MODE_DISABLE_MOUSE; + break; + case KBD_CCMD_MOUSE_ENABLE: + s->mode &= ~KBD_MODE_DISABLE_MOUSE; + break; + case KBD_CCMD_TEST_MOUSE: + kbd_queue(s, 0x00, 0); + break; + case KBD_CCMD_SELF_TEST: + s->status |= KBD_STAT_SELFTEST; + kbd_queue(s, 0x55, 0); + break; + case KBD_CCMD_KBD_TEST: + kbd_queue(s, 0x00, 0); + break; + case KBD_CCMD_KBD_DISABLE: + s->mode |= KBD_MODE_DISABLE_KBD; + kbd_update_irq(s); + break; + case KBD_CCMD_KBD_ENABLE: + s->mode &= ~KBD_MODE_DISABLE_KBD; + kbd_update_irq(s); + break; + case KBD_CCMD_READ_INPORT: + kbd_queue(s, 0x00, 0); + break; + case KBD_CCMD_READ_OUTPORT: + /* XXX: check that */ +#ifdef TARGET_I386 + val = 0x01 | (ioport_get_a20() << 1); +#else + val = 0x01; +#endif + if (s->status & KBD_STAT_OBF) + val |= 0x10; + if (s->status & KBD_STAT_MOUSE_OBF) + val |= 0x20; + kbd_queue(s, val, 0); + break; +#ifdef TARGET_I386 + case KBD_CCMD_ENABLE_A20: + ioport_set_a20(1); + break; + case KBD_CCMD_DISABLE_A20: + ioport_set_a20(0); + break; +#endif + case KBD_CCMD_RESET: + qemu_system_reset_request(); + break; + case 0xff: + /* ignore that - I don't know what is its use */ + break; + default: + fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", val); + break; + } +} + +static uint32_t kbd_read_data(void *opaque, uint32_t addr) +{ + KBDState *s = opaque; + + if (s->pending == KBD_PENDING_AUX) + return ps2_read_data(s->mouse); + + return ps2_read_data(s->kbd); +} + +void kbd_write_data(void *opaque, uint32_t addr, uint32_t val) +{ + KBDState *s = opaque; + +#ifdef DEBUG_KBD + printf("kbd: write data=0x%02x\n", val); +#endif + + switch(s->write_cmd) { + case 0: + ps2_write_keyboard(s->kbd, val); + break; + case KBD_CCMD_WRITE_MODE: + s->mode = val; + ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0); + /* ??? */ + kbd_update_irq(s); + break; + case KBD_CCMD_WRITE_OBUF: + kbd_queue(s, val, 0); + break; + case KBD_CCMD_WRITE_AUX_OBUF: + kbd_queue(s, val, 1); + break; + case KBD_CCMD_WRITE_OUTPORT: +#ifdef TARGET_I386 + ioport_set_a20((val >> 1) & 1); +#endif + if (!(val & 1)) { + qemu_system_reset_request(); + } + break; + case KBD_CCMD_WRITE_MOUSE: + ps2_write_mouse(s->mouse, val); + break; + default: + break; + } + s->write_cmd = 0; +} + +static void kbd_reset(void *opaque) +{ + KBDState *s = opaque; + + s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT; + s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED; +} + +static void kbd_save(QEMUFile* f, void* opaque) +{ + KBDState *s = (KBDState*)opaque; + + qemu_put_8s(f, &s->write_cmd); + qemu_put_8s(f, &s->status); + qemu_put_8s(f, &s->mode); + qemu_put_8s(f, &s->pending); +} + +static int kbd_load(QEMUFile* f, void* opaque, int version_id) +{ + KBDState *s = (KBDState*)opaque; + + if (version_id != 3) + return -EINVAL; + qemu_get_8s(f, &s->write_cmd); + qemu_get_8s(f, &s->status); + qemu_get_8s(f, &s->mode); + qemu_get_8s(f, &s->pending); + return 0; +} + +void kbd_init(void) +{ + KBDState *s = &kbd_state; + + kbd_reset(s); + register_savevm("pckbd", 0, 3, kbd_save, kbd_load, s); + register_ioport_read(0x60, 1, 1, kbd_read_data, s); + register_ioport_write(0x60, 1, 1, kbd_write_data, s); + register_ioport_read(0x64, 1, 1, kbd_read_status, s); + register_ioport_write(0x64, 1, 1, kbd_write_command, s); + + s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s); + s->mouse = ps2_mouse_init(kbd_update_aux_irq, s); + qemu_register_reset(kbd_reset, s); +} diff --git a/tools/ioemu/hw/pcspk.c b/tools/ioemu/hw/pcspk.c new file mode 100644 index 0000000000..2e30662a25 --- /dev/null +++ b/tools/ioemu/hw/pcspk.c @@ -0,0 +1,147 @@ +/* + * QEMU PC speaker emulation + * + * Copyright (c) 2006 Joachim Henke + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "vl.h" + +#define PCSPK_BUF_LEN 1792 +#define PCSPK_SAMPLE_RATE 32000 +#define PCSPK_MAX_FREQ (PCSPK_SAMPLE_RATE >> 1) +#define PCSPK_MIN_COUNT ((PIT_FREQ + PCSPK_MAX_FREQ - 1) / PCSPK_MAX_FREQ) + +typedef struct { + uint8_t sample_buf[PCSPK_BUF_LEN]; + QEMUSoundCard card; + SWVoiceOut *voice; + PITState *pit; + unsigned int pit_count; + unsigned int samples; + unsigned int play_pos; + int data_on; + int dummy_refresh_clock; +} PCSpkState; + +static const char *s_spk = "pcspk"; +static PCSpkState pcspk_state; + +static inline void generate_samples(PCSpkState *s) +{ + unsigned int i; + + if (s->pit_count) { + const uint32_t m = PCSPK_SAMPLE_RATE * s->pit_count; + const uint32_t n = ((uint64_t)PIT_FREQ << 32) / m; + + /* multiple of wavelength for gapless looping */ + s->samples = (PCSPK_BUF_LEN * PIT_FREQ / m * m / (PIT_FREQ >> 1) + 1) >> 1; + for (i = 0; i < s->samples; ++i) + s->sample_buf[i] = (64 & (n * i >> 25)) - 32; + } else { + s->samples = PCSPK_BUF_LEN; + for (i = 0; i < PCSPK_BUF_LEN; ++i) + s->sample_buf[i] = 128; /* silence */ + } +} + +static void pcspk_callback(void *opaque, int free) +{ + PCSpkState *s = opaque; + unsigned int n; + + if (pit_get_mode(s->pit, 2) != 3) + return; + + n = pit_get_initial_count(s->pit, 2); + /* avoid frequencies that are not reproducible with sample rate */ + if (n < PCSPK_MIN_COUNT) + n = 0; + + if (s->pit_count != n) { + s->pit_count = n; + s->play_pos = 0; + generate_samples(s); + } + + while (free > 0) { + n = audio_MIN(s->samples - s->play_pos, (unsigned int)free); + n = AUD_write(s->voice, &s->sample_buf[s->play_pos], n); + if (!n) + break; + s->play_pos = (s->play_pos + n) % s->samples; + free -= n; + } +} + +int pcspk_audio_init(AudioState *audio) +{ + PCSpkState *s = &pcspk_state; + audsettings_t as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8}; + + if (!audio) { + AUD_log(s_spk, "No audio state\n"); + return -1; + } + AUD_register_card(audio, s_spk, &s->card); + + s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, pcspk_callback, &as, 0); + if (!s->voice) { + AUD_log(s_spk, "Could not open voice\n"); + return -1; + } + + return 0; +} + +static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr) +{ + PCSpkState *s = opaque; + int out; + + s->dummy_refresh_clock ^= (1 << 4); + out = pit_get_out(s->pit, 2, qemu_get_clock(vm_clock)) << 5; + + return pit_get_gate(s->pit, 2) | (s->data_on << 1) | s->dummy_refresh_clock | out; +} + +static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + PCSpkState *s = opaque; + const int gate = val & 1; + + s->data_on = (val >> 1) & 1; + pit_set_gate(s->pit, 2, gate); + if (s->voice) { + if (gate) /* restart */ + s->play_pos = 0; + AUD_set_active_out(s->voice, gate & s->data_on); + } +} + +void pcspk_init(PITState *pit) +{ + PCSpkState *s = &pcspk_state; + + s->pit = pit; + register_ioport_read(0x61, 1, 1, pcspk_ioport_read, s); + register_ioport_write(0x61, 1, 1, pcspk_ioport_write, s); +} diff --git a/tools/ioemu/hw/piix4acpi.c b/tools/ioemu/hw/piix4acpi.c new file mode 100644 index 0000000000..530ce2c531 --- /dev/null +++ b/tools/ioemu/hw/piix4acpi.c @@ -0,0 +1,484 @@ +/* + * PIIX4 ACPI controller emulation + * + * Winston liwen Wang, winston.l.wang@intel.com + * Copyright (c) 2006 , Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "vl.h" +#define FREQUENCE_PMTIMER 3753425 +/* acpi register bit define here */ + +/* PM1_STS */ +#define TMROF_STS (1 << 0) +#define BM_STS (1 << 4) +#define GBL_STS (1 << 5) +#define PWRBTN_STS (1 << 8) +#define RTC_STS (1 << 10) +#define PRBTNOR_STS (1 << 11) +#define WAK_STS (1 << 15) +/* PM1_EN */ +#define TMROF_EN (1 << 0) +#define GBL_EN (1 << 5) +#define PWRBTN_EN (1 << 8) +#define RTC_EN (1 << 10) +/* PM1_CNT */ +#define SCI_EN (1 << 0) +#define GBL_RLS (1 << 2) +#define SLP_EN (1 << 13) + +/* Bits of PM1a register define here */ +#define SLP_TYP_MASK 0x1C00 +#define SLP_VAL 0x1C00 + +typedef struct AcpiDeviceState AcpiDeviceState; +AcpiDeviceState *acpi_device_table; + +/* Bits of PM1a register define here */ +typedef struct PMTState { + uint32_t count; + int irq; + uint64_t next_pm_time; + QEMUTimer *pm_timer; +}PMTState; + +typedef struct PM1Event_BLK { + uint16_t pm1_status; /* pm1a_EVT_BLK */ + uint16_t pm1_enable; /* pm1a_EVT_BLK+2 */ +}PM1Event_BLK; + +typedef struct PCIAcpiState { + PCIDevice dev; + uint16_t irq; + uint16_t pm1_status; /* pm1a_EVT_BLK */ + uint16_t pm1_enable; /* pm1a_EVT_BLK+2 */ + uint16_t pm1_control; /* pm1a_ECNT_BLK */ + uint32_t pm1_timer; /* pmtmr_BLK */ +} PCIAcpiState; + +static PMTState *pmtimer_state; +static PCIAcpiState *acpi_state; + +static void pmtimer_save(QEMUFile *f, void *opaque) +{ + PMTState *s = opaque; + + qemu_put_be32s(f, &s->count); + qemu_put_be32s(f, &s->irq); + qemu_put_be64s(f, &s->next_pm_time); + qemu_put_timer(f, s->pm_timer); +} + +static int pmtimer_load(QEMUFile *f, void *opaque, int version_id) +{ + PMTState *s = opaque; + + if (version_id != 1) + return -EINVAL; + qemu_get_be32s(f, &s->count); + qemu_get_be32s(f, &s->irq); + qemu_get_be64s(f, &s->next_pm_time); + qemu_get_timer(f, s->pm_timer); + return 0; +} + +static inline void acpi_set_irq(PCIAcpiState *s) +{ +/* no real SCI event need for now, so comment the following line out */ +/* pic_set_irq(s->irq, 1); */ + printf("acpi_set_irq: s->irq %x \n",s->irq); +} + +static void pm_timer_update(void *opaque) +{ + PMTState *s = opaque; + s->next_pm_time = qemu_get_clock(vm_clock) + + muldiv64(1, ticks_per_sec,FREQUENCE_PMTIMER); + qemu_mod_timer(s->pm_timer, s->next_pm_time); + acpi_state->pm1_timer ++; + + /* If pm timer is zero then reset it to zero. */ + if (acpi_state->pm1_timer >= 0x1000000) { +/* printf("pm_timerupdate: timer overflow: %x \n", acpi_state->pm1_timer); */ + + acpi_state->pm1_timer = 0; + acpi_state->pm1_status = acpi_state->pm1_status | TMROF_STS; + /* If TMROF_EN is set then send the irq. */ + if ((acpi_state->pm1_enable & TMROF_EN) == TMROF_EN) { + acpi_set_irq(acpi_state); + acpi_state->pm1_enable = 0x00; /* only need one time...*/ + } + } + s->count = acpi_state->pm1_timer; +} + +static PMTState *pmtimer_init(void) +{ + PMTState *s; + + s = qemu_mallocz(sizeof(PMTState)); + if (!s) + return NULL; + + /* s->irq = irq; */ + + s->pm_timer = qemu_new_timer(vm_clock, pm_timer_update, s); + + s->count = 0; + s->next_pm_time = qemu_get_clock(vm_clock) + muldiv64(1, ticks_per_sec,FREQUENCE_PMTIMER) + 1; + qemu_mod_timer(s->pm_timer, s->next_pm_time); + + register_savevm("pm timer", 1, 1, pmtimer_save, pmtimer_load, s); + return s; +} + +static void acpi_reset(PCIAcpiState *s) +{ + uint8_t *pci_conf; + pci_conf = s->dev.config; + + pci_conf[0x42] = 0x00; + pci_conf[0x43] = 0x00; + s->irq = 9; + s->pm1_status = 0; + s->pm1_enable = 0x00; /* TMROF_EN should cleared */ + s->pm1_control = SCI_EN; /* SCI_EN */ + s->pm1_timer = 0; +} + +/*byte access */ +static void acpiPm1Status_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + PCIAcpiState *s = opaque; + + if ((val&TMROF_STS)==TMROF_STS) + s->pm1_status = s->pm1_status&!TMROF_STS; + + if ((val&GBL_STS)==GBL_STS) + s->pm1_status = s->pm1_status&!GBL_STS; + +/* printf("acpiPm1Status_writeb \n addr %x val:%x pm1_status:%x \n", addr, val,s->pm1_status); */ +} + +static uint32_t acpiPm1Status_readb(void *opaque, uint32_t addr) +{ + PCIAcpiState *s = opaque; + uint32_t val; + + val = s->pm1_status; +/* printf("acpiPm1Status_readb \n addr %x val:%x\n", addr, val); */ + + return val; +} + +static void acpiPm1StatusP1_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + PCIAcpiState *s = opaque; + + s->pm1_status = (val<<8)||(s->pm1_status); +/* printf("acpiPm1StatusP1_writeb \n addr %x val:%x\n", addr, val); */ +} + +static uint32_t acpiPm1StatusP1_readb(void *opaque, uint32_t addr) +{ + PCIAcpiState *s = opaque; + uint32_t val; + + val = (s->pm1_status)>>8; + printf("acpiPm1StatusP1_readb \n addr %x val:%x\n", addr, val); + + return val; +} + +static void acpiPm1Enable_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + PCIAcpiState *s = opaque; + + s->pm1_enable = val; +/* printf("acpiPm1Enable_writeb \n addr %x val:%x\n", addr, val); */ +} + +static uint32_t acpiPm1Enable_readb(void *opaque, uint32_t addr) +{ + PCIAcpiState *s = opaque; + uint32_t val; + + val = (s->pm1_enable)||0x1; +/* printf("acpiPm1Enable_readb \n addr %x val:%x\n", addr, val); */ + + return val; +} + +static void acpiPm1EnableP1_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + PCIAcpiState *s = opaque; + + s->pm1_enable = (val<<8)||(s->pm1_enable); +/* printf("acpiPm1EnableP1_writeb \n addr %x val:%x\n", addr, val); */ + +} + +static uint32_t acpiPm1EnableP1_readb(void *opaque, uint32_t addr) +{ + PCIAcpiState *s = opaque; + uint32_t val; + + val = (s->pm1_enable)>>8; +/* printf("acpiPm1EnableP1_readb \n addr %x val:%x\n", addr, val); */ + + return val; +} + +static void acpiPm1Control_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + PCIAcpiState *s = opaque; + + s->pm1_control = val; +/* printf("acpiPm1Control_writeb \n addr %x val:%x\n", addr, val); */ + +} + +static uint32_t acpiPm1Control_readb(void *opaque, uint32_t addr) +{ + PCIAcpiState *s = opaque; + uint32_t val; + + val = s->pm1_control; +/* printf("acpiPm1Control_readb \n addr %x val:%x\n", addr, val); */ + + return val; +} + +static void acpiPm1ControlP1_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + PCIAcpiState *s = opaque; + + s->pm1_control = (val<<8)||(s->pm1_control); +/* printf("acpiPm1ControlP1_writeb \n addr %x val:%x\n", addr, val); */ + + // Check for power off request + + if (((val & SLP_EN) != 0) && + ((val & SLP_TYP_MASK) == SLP_VAL)) { + s->pm1_timer=0x0; //clear ACPI timer + qemu_system_shutdown_request(); + } +} + +static uint32_t acpiPm1ControlP1_readb(void *opaque, uint32_t addr) +{ + PCIAcpiState *s = opaque; + uint32_t val; + + val = (s->pm1_control)>>8; +/* printf("acpiPm1ControlP1_readb \n addr %x val:%x\n", addr, val); */ + + return val; +} + + +/* word access */ + +static void acpiPm1Status_writew(void *opaque, uint32_t addr, uint32_t val) +{ + PCIAcpiState *s = opaque; + + if ((val&TMROF_STS)==TMROF_STS) + s->pm1_status = s->pm1_status&!TMROF_STS; + + if ((val&GBL_STS)==GBL_STS) + s->pm1_status = s->pm1_status&!GBL_STS; + +/* printf("acpiPm1Status_writew \n addr %x val:%x pm1_status:%x \n", addr, val,s->pm1_status); */ +} + +static uint32_t acpiPm1Status_readw(void *opaque, uint32_t addr) +{ + PCIAcpiState *s = opaque; + uint32_t val; + + val = s->pm1_status; +/* printf("acpiPm1Status_readw \n addr %x val:%x\n", addr, val); */ + + return val; +} + +static void acpiPm1Enable_writew(void *opaque, uint32_t addr, uint32_t val) +{ + PCIAcpiState *s = opaque; + + s->pm1_enable = val; +/* printf("acpiPm1Enable_writew \n addr %x val:%x\n", addr, val); */ + +} + +static uint32_t acpiPm1Enable_readw(void *opaque, uint32_t addr) +{ + PCIAcpiState *s = opaque; + uint32_t val; + + val = s->pm1_enable; +/* printf("acpiPm1Enable_readw \n addr %x val:%x\n", addr, val); */ + + return val; +} + +static void acpiPm1Control_writew(void *opaque, uint32_t addr, uint32_t val) +{ + PCIAcpiState *s = opaque; + + s->pm1_control = val; +/* printf("acpiPm1Control_writew \n addr %x val:%x\n", addr, val); */ + + // Check for power off request + + if (((val & SLP_EN) != 0) && + ((val & SLP_TYP_MASK) == SLP_VAL)) { + qemu_system_shutdown_request(); + } + +} + +static uint32_t acpiPm1Control_readw(void *opaque, uint32_t addr) +{ + PCIAcpiState *s = opaque; + uint32_t val; + + val = s->pm1_control; +/* printf("acpiPm1Control_readw \n addr %x val:%x\n", addr, val); */ + + return val; +} + +/* dword access */ + +static void acpiPm1Event_writel(void *opaque, uint32_t addr, uint32_t val) +{ + PCIAcpiState *s = opaque; + + s->pm1_status = val; + s->pm1_enable = val>>16; +/* printf("acpiPm1Event_writel \n addr %x val:%x \n", addr, val); */ + +} + +static uint32_t acpiPm1Event_readl(void *opaque, uint32_t addr) +{ + PCIAcpiState *s = opaque; + uint32_t val; + + val = s->pm1_status|(s->pm1_enable<<16); +/* printf("acpiPm1Event_readl \n addr %x val:%x\n", addr, val); */ + + return val; +} + +static void acpiPm1Timer_writel(void *opaque, uint32_t addr, uint32_t val) +{ + PCIAcpiState *s = opaque; + + s->pm1_timer = val; +/* printf("acpiPm1Timer_writel \n addr %x val:%x\n", addr, val); */ +} + +static uint32_t acpiPm1Timer_readl(void *opaque, uint32_t addr) +{ + PCIAcpiState *s = opaque; + uint32_t val; + + val = s->pm1_timer; +/* printf("acpiPm1Timer_readl \n addr %x val:%x\n", addr, val); */ + return val; +} + +static void acpi_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + PCIAcpiState *d = (PCIAcpiState *)pci_dev; + + printf("register acpi io \n"); + + /* Byte access */ + register_ioport_write(addr, 1, 1, acpiPm1Status_writeb, d); + register_ioport_read(addr, 1, 1, acpiPm1Status_readb, d); + register_ioport_write(addr+1, 1, 1, acpiPm1StatusP1_writeb, d); + register_ioport_read(addr+1, 1, 1, acpiPm1StatusP1_readb, d); + + register_ioport_write(addr + 2, 1, 1, acpiPm1Enable_writeb, d); + register_ioport_read(addr + 2, 1, 1, acpiPm1Enable_readb, d); + register_ioport_write(addr + 2 +1, 1, 1, acpiPm1EnableP1_writeb, d); + register_ioport_read(addr + 2 +1, 1, 1, acpiPm1EnableP1_readb, d); + + register_ioport_write(addr + 4, 1, 1, acpiPm1Control_writeb, d); + register_ioport_read(addr + 4, 1, 1, acpiPm1Control_readb, d); + register_ioport_write(addr + 4 + 1, 1, 1, acpiPm1ControlP1_writeb, d); + register_ioport_read(addr + 4 +1, 1, 1, acpiPm1ControlP1_readb, d); + + /* Word access */ + register_ioport_write(addr, 2, 2, acpiPm1Status_writew, d); + register_ioport_read(addr, 2, 2, acpiPm1Status_readw, d); + + register_ioport_write(addr + 2, 2, 2, acpiPm1Enable_writew, d); + register_ioport_read(addr + 2, 2, 2, acpiPm1Enable_readw, d); + + register_ioport_write(addr + 4, 2, 2, acpiPm1Control_writew, d); + register_ioport_read(addr + 4, 2, 2, acpiPm1Control_readw, d); + + /* DWord access */ + register_ioport_write(addr, 4, 4, acpiPm1Event_writel, d); + register_ioport_read(addr, 4, 4, acpiPm1Event_readl, d); + + register_ioport_write(addr + 8, 4, 4, acpiPm1Timer_writel, d); + register_ioport_read(addr + 8, 4, 4, acpiPm1Timer_readl, d); +} + + +/* PIIX4 acpi pci configuration space, func 3 */ +void pci_piix4_acpi_init(PCIBus *bus) +{ + PCIAcpiState *d; + uint8_t *pci_conf; + + /* register a function 3 of PIIX4 */ + d = (PCIAcpiState *)pci_register_device( + bus, "PIIX4 ACPI", sizeof(PCIAcpiState), + ((PCIDevice *)piix3_state)->devfn + 3, NULL, NULL); + + acpi_state = d; + pci_conf = d->dev.config; + pci_conf[0x00] = 0x86; /* Intel */ + pci_conf[0x01] = 0x80; + pci_conf[0x02] = 0x13; + pci_conf[0x03] = 0x71; + pci_conf[0x08] = 0x01; /* B0 stepping */ + pci_conf[0x09] = 0x00; /* base class */ + pci_conf[0x0a] = 0x80; /* Sub class */ + pci_conf[0x0b] = 0x06; + pci_conf[0x0e] = 0x00; + pci_conf[0x3d] = 0x01; /* Hardwired to PIRQA is used */ + + pci_register_io_region((PCIDevice *)d, 4, 0x10, + PCI_ADDRESS_SPACE_IO, acpi_map); + + pmtimer_state = pmtimer_init(); + + acpi_reset (d); +} diff --git a/tools/ioemu/hw/pl011.c b/tools/ioemu/hw/pl011.c new file mode 100644 index 0000000000..657f03bbe8 --- /dev/null +++ b/tools/ioemu/hw/pl011.c @@ -0,0 +1,251 @@ +/* + * Arm PrimeCell PL011 UART + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GPL. + */ + +#include "vl.h" + +typedef struct { + uint32_t base; + uint32_t readbuff; + uint32_t flags; + uint32_t lcr; + uint32_t cr; + uint32_t dmacr; + uint32_t int_enabled; + uint32_t int_level; + uint32_t read_fifo[16]; + uint32_t ilpr; + uint32_t ibrd; + uint32_t fbrd; + uint32_t ifl; + int read_pos; + int read_count; + int read_trigger; + CharDriverState *chr; + void *pic; + int irq; +} pl011_state; + +#define PL011_INT_TX 0x20 +#define PL011_INT_RX 0x10 + +#define PL011_FLAG_TXFE 0x80 +#define PL011_FLAG_RXFF 0x40 +#define PL011_FLAG_TXFF 0x20 +#define PL011_FLAG_RXFE 0x10 + +static const unsigned char pl011_id[] = +{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; + +static void pl011_update(pl011_state *s) +{ + uint32_t flags; + + flags = s->int_level & s->int_enabled; + pic_set_irq_new(s->pic, s->irq, flags != 0); +} + +static uint32_t pl011_read(void *opaque, target_phys_addr_t offset) +{ + pl011_state *s = (pl011_state *)opaque; + uint32_t c; + + offset -= s->base; + if (offset >= 0xfe0 && offset < 0x1000) { + return pl011_id[(offset - 0xfe0) >> 2]; + } + switch (offset >> 2) { + case 0: /* UARTDR */ + s->flags &= ~PL011_FLAG_RXFF; + c = s->read_fifo[s->read_pos]; + if (s->read_count > 0) { + s->read_count--; + if (++s->read_pos == 16) + s->read_pos = 0; + } + if (s->read_count == 0) { + s->flags |= PL011_FLAG_RXFE; + } + if (s->read_count == s->read_trigger - 1) + s->int_level &= ~ PL011_INT_RX; + pl011_update(s); + return c; + case 1: /* UARTCR */ + return 0; + case 6: /* UARTFR */ + return s->flags; + case 8: /* UARTILPR */ + return s->ilpr; + case 9: /* UARTIBRD */ + return s->ibrd; + case 10: /* UARTFBRD */ + return s->fbrd; + case 11: /* UARTLCR_H */ + return s->lcr; + case 12: /* UARTCR */ + return s->cr; + case 13: /* UARTIFLS */ + return s->ifl; + case 14: /* UARTIMSC */ + return s->int_enabled; + case 15: /* UARTRIS */ + return s->int_level; + case 16: /* UARTMIS */ + return s->int_level & s->int_enabled; + case 18: /* UARTDMACR */ + return s->dmacr; + default: + cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", offset); + return 0; + } +} + +static void pl011_set_read_trigger(pl011_state *s) +{ +#if 0 + /* The docs say the RX interrupt is triggered when the FIFO exceeds + the threshold. However linux only reads the FIFO in response to an + interrupt. Triggering the interrupt when the FIFO is non-empty seems + to make things work. */ + if (s->lcr & 0x10) + s->read_trigger = (s->ifl >> 1) & 0x1c; + else +#endif + s->read_trigger = 1; +} + +static void pl011_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + pl011_state *s = (pl011_state *)opaque; + unsigned char ch; + + offset -= s->base; + switch (offset >> 2) { + case 0: /* UARTDR */ + /* ??? Check if transmitter is enabled. */ + ch = value; + if (s->chr) + qemu_chr_write(s->chr, &ch, 1); + s->int_level |= PL011_INT_TX; + pl011_update(s); + break; + case 1: /* UARTCR */ + s->cr = value; + break; + case 8: /* UARTUARTILPR */ + s->ilpr = value; + break; + case 9: /* UARTIBRD */ + s->ibrd = value; + break; + case 10: /* UARTFBRD */ + s->fbrd = value; + break; + case 11: /* UARTLCR_H */ + s->lcr = value; + pl011_set_read_trigger(s); + break; + case 12: /* UARTCR */ + /* ??? Need to implement the enable and loopback bits. */ + s->cr = value; + break; + case 13: /* UARTIFS */ + s->ifl = value; + pl011_set_read_trigger(s); + break; + case 14: /* UARTIMSC */ + s->int_enabled = value; + pl011_update(s); + break; + case 17: /* UARTICR */ + s->int_level &= ~value; + pl011_update(s); + break; + case 18: /* UARTDMACR */ + s->dmacr = value; + if (value & 3) + cpu_abort(cpu_single_env, "PL011: DMA not implemented\n"); + break; + default: + cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", offset); + } +} + +static int pl011_can_recieve(void *opaque) +{ + pl011_state *s = (pl011_state *)opaque; + + if (s->lcr & 0x10) + return s->read_count < 16; + else + return s->read_count < 1; +} + +static void pl011_recieve(void *opaque, const uint8_t *buf, int size) +{ + pl011_state *s = (pl011_state *)opaque; + int slot; + + slot = s->read_pos + s->read_count; + if (slot >= 16) + slot -= 16; + s->read_fifo[slot] = *buf; + s->read_count++; + s->flags &= ~PL011_FLAG_RXFE; + if (s->cr & 0x10 || s->read_count == 16) { + s->flags |= PL011_FLAG_RXFF; + } + if (s->read_count == s->read_trigger) { + s->int_level |= PL011_INT_RX; + pl011_update(s); + } +} + +static void pl011_event(void *opaque, int event) +{ + /* ??? Should probably implement break. */ +} + +static CPUReadMemoryFunc *pl011_readfn[] = { + pl011_read, + pl011_read, + pl011_read +}; + +static CPUWriteMemoryFunc *pl011_writefn[] = { + pl011_write, + pl011_write, + pl011_write +}; + +void pl011_init(uint32_t base, void *pic, int irq, + CharDriverState *chr) +{ + int iomemtype; + pl011_state *s; + + s = (pl011_state *)qemu_mallocz(sizeof(pl011_state)); + iomemtype = cpu_register_io_memory(0, pl011_readfn, + pl011_writefn, s); + cpu_register_physical_memory(base, 0x00000fff, iomemtype); + s->base = base; + s->pic = pic; + s->irq = irq; + s->chr = chr; + s->read_trigger = 1; + s->ifl = 0x12; + s->cr = 0x300; + s->flags = 0x90; + if (chr){ + qemu_chr_add_read_handler(chr, pl011_can_recieve, pl011_recieve, s); + qemu_chr_add_event_handler(chr, pl011_event); + } + /* ??? Save/restore. */ +} + diff --git a/tools/ioemu/hw/pl050.c b/tools/ioemu/hw/pl050.c new file mode 100644 index 0000000000..a71ccf6149 --- /dev/null +++ b/tools/ioemu/hw/pl050.c @@ -0,0 +1,127 @@ +/* + * Arm PrimeCell PL050 Kyeboard / Mouse Interface + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GPL. + */ + +#include "vl.h" + +typedef struct { + void *dev; + uint32_t base; + uint32_t cr; + uint32_t clk; + uint32_t last; + void *pic; + int pending; + int irq; + int is_mouse; +} pl050_state; + +static const unsigned char pl050_id[] = +{ 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; + +static void pl050_update(void *opaque, int level) +{ + pl050_state *s = (pl050_state *)opaque; + int raise; + + s->pending = level; + raise = (s->pending && (s->cr & 0x10) != 0) + || (s->cr & 0x08) != 0; + pic_set_irq_new(s->pic, s->irq, raise); +} + +static uint32_t pl050_read(void *opaque, target_phys_addr_t offset) +{ + pl050_state *s = (pl050_state *)opaque; + offset -= s->base; + if (offset >= 0xfe0 && offset < 0x1000) + return pl050_id[(offset - 0xfe0) >> 2]; + + switch (offset >> 2) { + case 0: /* KMICR */ + return s->cr; + case 1: /* KMISTAT */ + /* KMIC and KMID bits not implemented. */ + if (s->pending) { + return 0x10; + } else { + return 0; + } + case 2: /* KMIDATA */ + if (s->pending) + s->last = ps2_read_data(s->dev); + return s->last; + case 3: /* KMICLKDIV */ + return s->clk; + case 4: /* KMIIR */ + return s->pending | 2; + default: + cpu_abort (cpu_single_env, "pl050_read: Bad offset %x\n", offset); + return 0; + } +} + +static void pl050_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + pl050_state *s = (pl050_state *)opaque; + offset -= s->base; + switch (offset >> 2) { + case 0: /* KMICR */ + s->cr = value; + pl050_update(s, s->pending); + /* ??? Need to implement the enable/disable bit. */ + break; + case 2: /* KMIDATA */ + /* ??? This should toggle the TX interrupt line. */ + /* ??? This means kbd/mouse can block each other. */ + if (s->is_mouse) { + ps2_write_mouse(s->dev, value); + } else { + ps2_write_keyboard(s->dev, value); + } + break; + case 3: /* KMICLKDIV */ + s->clk = value; + return; + default: + cpu_abort (cpu_single_env, "pl050_write: Bad offset %x\n", offset); + } +} +static CPUReadMemoryFunc *pl050_readfn[] = { + pl050_read, + pl050_read, + pl050_read +}; + +static CPUWriteMemoryFunc *pl050_writefn[] = { + pl050_write, + pl050_write, + pl050_write +}; + +void pl050_init(uint32_t base, void *pic, int irq, int is_mouse) +{ + int iomemtype; + pl050_state *s; + + s = (pl050_state *)qemu_mallocz(sizeof(pl050_state)); + iomemtype = cpu_register_io_memory(0, pl050_readfn, + pl050_writefn, s); + cpu_register_physical_memory(base, 0x00000fff, iomemtype); + s->base = base; + s->pic = pic; + s->irq = irq; + s->is_mouse = is_mouse; + if (is_mouse) + s->dev = ps2_mouse_init(pl050_update, s); + else + s->dev = ps2_kbd_init(pl050_update, s); + /* ??? Save/restore. */ +} + diff --git a/tools/ioemu/hw/pl080.c b/tools/ioemu/hw/pl080.c new file mode 100644 index 0000000000..49996ca91d --- /dev/null +++ b/tools/ioemu/hw/pl080.c @@ -0,0 +1,328 @@ +/* + * Arm PrimeCell PL080 DMA controller + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GPL. + */ + +#include "vl.h" + +#define PL080_NUM_CHANNELS 8 +#define PL080_CONF_E 0x1 +#define PL080_CONF_M1 0x2 +#define PL080_CONF_M2 0x4 + +#define PL080_CCONF_H 0x40000 +#define PL080_CCONF_A 0x20000 +#define PL080_CCONF_L 0x10000 +#define PL080_CCONF_ITC 0x08000 +#define PL080_CCONF_IE 0x04000 +#define PL080_CCONF_E 0x00001 + +#define PL080_CCTRL_I 0x80000000 +#define PL080_CCTRL_DI 0x08000000 +#define PL080_CCTRL_SI 0x04000000 +#define PL080_CCTRL_D 0x02000000 +#define PL080_CCTRL_S 0x01000000 + +typedef struct { + uint32_t src; + uint32_t dest; + uint32_t lli; + uint32_t ctrl; + uint32_t conf; +} pl080_channel; + +typedef struct { + uint32_t base; + uint8_t tc_int; + uint8_t tc_mask; + uint8_t err_int; + uint8_t err_mask; + uint32_t conf; + uint32_t sync; + uint32_t req_single; + uint32_t req_burst; + pl080_channel chan[PL080_NUM_CHANNELS]; + /* Flag to avoid recursive DMA invocations. */ + int running; + void *pic; + int irq; +} pl080_state; + +static const unsigned char pl080_id[] = +{ 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 }; + +static void pl080_update(pl080_state *s) +{ + if ((s->tc_int & s->tc_mask) + || (s->err_int & s->err_mask)) + pic_set_irq_new(s->pic, s->irq, 1); + else + pic_set_irq_new(s->pic, s->irq, 1); +} + +static void pl080_run(pl080_state *s) +{ + int c; + int flow; + pl080_channel *ch; + int swidth; + int dwidth; + int xsize; + int n; + int src_id; + int dest_id; + int size; + char buff[4]; + uint32_t req; + + s->tc_mask = 0; + for (c = 0; c < PL080_NUM_CHANNELS; c++) { + if (s->chan[c].conf & PL080_CCONF_ITC) + s->tc_mask |= 1 << c; + if (s->chan[c].conf & PL080_CCONF_IE) + s->err_mask |= 1 << c; + } + + if ((s->conf & PL080_CONF_E) == 0) + return; + +cpu_abort(cpu_single_env, "DMA active\n"); + /* If we are already in the middle of a DMA operation then indicate that + there may be new DMA requests and return immediately. */ + if (s->running) { + s->running++; + return; + } + s->running = 1; + while (s->running) { + for (c = 0; c < PL080_NUM_CHANNELS; c++) { + ch = &s->chan[c]; +again: + /* Test if thiws channel has any pending DMA requests. */ + if ((ch->conf & (PL080_CCONF_H | PL080_CCONF_E)) + != PL080_CCONF_E) + continue; + flow = (ch->conf >> 11) & 7; + if (flow >= 4) { + cpu_abort(cpu_single_env, + "pl080_run: Peripheral flow control not implemented\n"); + } + src_id = (ch->conf >> 1) & 0x1f; + dest_id = (ch->conf >> 6) & 0x1f; + size = ch->ctrl & 0xfff; + req = s->req_single | s->req_burst; + switch (flow) { + case 0: + break; + case 1: + if ((req & (1u << dest_id)) == 0) + size = 0; + break; + case 2: + if ((req & (1u << src_id)) == 0) + size = 0; + break; + case 3: + if ((req & (1u << src_id)) == 0 + || (req & (1u << dest_id)) == 0) + size = 0; + break; + } + if (!size) + continue; + + /* Transfer one element. */ + /* ??? Should transfer multiple elements for a burst request. */ + /* ??? Unclear what the proper behavior is when source and + destination widths are different. */ + swidth = 1 << ((ch->ctrl >> 18) & 7); + dwidth = 1 << ((ch->ctrl >> 21) & 7); + for (n = 0; n < dwidth; n+= swidth) { + cpu_physical_memory_read(ch->src, buff + n, swidth); + if (ch->ctrl & PL080_CCTRL_SI) + ch->src += swidth; + } + xsize = (dwidth < swidth) ? swidth : dwidth; + /* ??? This may pad the value incorrectly for dwidth < 32. */ + for (n = 0; n < xsize; n += dwidth) { + cpu_physical_memory_write(ch->dest + n, buff + n, dwidth); + if (ch->ctrl & PL080_CCTRL_DI) + ch->dest += swidth; + } + + size--; + ch->ctrl = (ch->ctrl & 0xfffff000) | size; + if (size == 0) { + /* Transfer complete. */ + if (ch->lli) { + ch->src = ldl_phys(ch->lli); + ch->dest = ldl_phys(ch->lli + 4); + ch->ctrl = ldl_phys(ch->lli + 12); + ch->lli = ldl_phys(ch->lli + 8); + } else { + ch->conf &= ~PL080_CCONF_E; + } + if (ch->ctrl & PL080_CCTRL_I) { + s->tc_int |= 1 << c; + } + } + goto again; + } + if (--s->running) + s->running = 1; + } +} + +static uint32_t pl080_read(void *opaque, target_phys_addr_t offset) +{ + pl080_state *s = (pl080_state *)opaque; + uint32_t i; + uint32_t mask; + + offset -= s->base; + if (offset >= 0xfe0 && offset < 0x1000) { + return pl080_id[(offset - 0xfe0) >> 2]; + } + if (offset >= 0x100 && offset < 0x200) { + i = (offset & 0xe0) >> 5; + switch (offset >> 2) { + case 0: /* SrcAddr */ + return s->chan[i].src; + case 1: /* DestAddr */ + return s->chan[i].dest; + case 2: /* LLI */ + return s->chan[i].lli; + case 3: /* Control */ + return s->chan[i].ctrl; + case 4: /* Configuration */ + return s->chan[i].conf; + default: + goto bad_offset; + } + } + switch (offset >> 2) { + case 0: /* IntStatus */ + return (s->tc_int & s->tc_mask) | (s->err_int & s->err_mask); + case 1: /* IntTCStatus */ + return (s->tc_int & s->tc_mask); + case 3: /* IntErrorStatus */ + return (s->err_int & s->err_mask); + case 5: /* RawIntTCStatus */ + return s->tc_int; + case 6: /* RawIntErrorStatus */ + return s->err_int; + case 7: /* EnbldChns */ + mask = 0; + for (i = 0; i < PL080_NUM_CHANNELS; i++) { + if (s->chan[i].conf & PL080_CCONF_E) + mask |= 1 << i; + } + return mask; + case 8: /* SoftBReq */ + case 9: /* SoftSReq */ + case 10: /* SoftLBReq */ + case 11: /* SoftLSReq */ + /* ??? Implement these. */ + return 0; + case 12: /* Configuration */ + return s->conf; + case 13: /* Sync */ + return s->sync; + default: + bad_offset: + cpu_abort(cpu_single_env, "pl080_read: Bad offset %x\n", offset); + return 0; + } +} + +static void pl080_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + pl080_state *s = (pl080_state *)opaque; + int i; + + offset -= s->base; + if (offset >= 0x100 && offset < 0x200) { + i = (offset & 0xe0) >> 5; + switch (offset >> 2) { + case 0: /* SrcAddr */ + s->chan[i].src = value; + break; + case 1: /* DestAddr */ + s->chan[i].dest = value; + break; + case 2: /* LLI */ + s->chan[i].lli = value; + break; + case 3: /* Control */ + s->chan[i].ctrl = value; + break; + case 4: /* Configuration */ + s->chan[i].conf = value; + pl080_run(s); + break; + } + } + switch (offset >> 2) { + case 2: /* IntTCClear */ + s->tc_int &= ~value; + break; + case 4: /* IntErrorClear */ + s->err_int &= ~value; + break; + case 8: /* SoftBReq */ + case 9: /* SoftSReq */ + case 10: /* SoftLBReq */ + case 11: /* SoftLSReq */ + /* ??? Implement these. */ + cpu_abort(cpu_single_env, "pl080_write: Soft DMA not implemented\n"); + break; + case 12: /* Configuration */ + s->conf = value; + if (s->conf & (PL080_CONF_M1 | PL080_CONF_M1)) { + cpu_abort(cpu_single_env, + "pl080_write: Big-endian DMA not implemented\n"); + } + pl080_run(s); + break; + case 13: /* Sync */ + s->sync = value; + break; + default: + cpu_abort(cpu_single_env, "pl080_write: Bad offset %x\n", offset); + } + pl080_update(s); +} + +static CPUReadMemoryFunc *pl080_readfn[] = { + pl080_read, + pl080_read, + pl080_read +}; + +static CPUWriteMemoryFunc *pl080_writefn[] = { + pl080_write, + pl080_write, + pl080_write +}; + +void *pl080_init(uint32_t base, void *pic, int irq) +{ + int iomemtype; + pl080_state *s; + + s = (pl080_state *)qemu_mallocz(sizeof(pl080_state)); + iomemtype = cpu_register_io_memory(0, pl080_readfn, + pl080_writefn, s); + cpu_register_physical_memory(base, 0x00000fff, iomemtype); + s->base = base; + s->pic = pic; + s->irq = irq; + /* ??? Save/restore. */ + return s; +} + diff --git a/tools/ioemu/hw/pl110.c b/tools/ioemu/hw/pl110.c new file mode 100644 index 0000000000..ecebe35eb3 --- /dev/null +++ b/tools/ioemu/hw/pl110.c @@ -0,0 +1,420 @@ +/* + * Arm PrimeCell PL110 Color LCD Controller + * + * Copyright (c) 2005-2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GNU LGPL + */ + +#include "vl.h" + +#define PL110_CR_EN 0x001 +#define PL110_CR_BEBO 0x200 +#define PL110_CR_BEPO 0x400 +#define PL110_CR_PWR 0x800 + +enum pl110_bppmode +{ + BPP_1, + BPP_2, + BPP_4, + BPP_8, + BPP_16, + BPP_32 +}; + +typedef struct { + uint32_t base; + DisplayState *ds; + /* The Versatile/PB uses a slightly modified PL110 controller. */ + int versatile; + void *pic; + uint32_t timing[4]; + uint32_t cr; + uint32_t upbase; + uint32_t lpbase; + uint32_t int_status; + uint32_t int_mask; + int cols; + int rows; + enum pl110_bppmode bpp; + int invalidate; + uint32_t pallette[256]; + uint32_t raw_pallette[128]; + int irq; +} pl110_state; + +static const unsigned char pl110_id[] = +{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; + +/* The Arm documentation (DDI0224C) says the CLDC on the Versatile board + has a different ID. However Linux only looks for the normal ID. */ +#if 0 +static const unsigned char pl110_versatile_id[] = +{ 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; +#else +#define pl110_versatile_id pl110_id +#endif + +static inline uint32_t rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b) +{ + return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6); +} + +static inline uint32_t rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b) +{ + return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); +} + +static inline uint32_t rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b) +{ + return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); +} + +static inline uint32_t rgb_to_pixel24(unsigned int r, unsigned int g, unsigned b) +{ + return (r << 16) | (g << 8) | b; +} + +static inline uint32_t rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b) +{ + return (r << 16) | (g << 8) | b; +} + +typedef void (*drawfn)(uint32_t *, uint8_t *, const uint8_t *, int); + +#define BITS 8 +#include "pl110_template.h" +#define BITS 15 +#include "pl110_template.h" +#define BITS 16 +#include "pl110_template.h" +#define BITS 24 +#include "pl110_template.h" +#define BITS 32 +#include "pl110_template.h" + +static int pl110_enabled(pl110_state *s) +{ + return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR); +} + +static void pl110_update_display(void *opaque) +{ + pl110_state *s = (pl110_state *)opaque; + drawfn* fntable; + drawfn fn; + uint32_t *pallette; + uint32_t addr; + uint32_t base; + int dest_width; + int src_width; + uint8_t *dest; + uint8_t *src; + int first, last = 0; + int dirty, new_dirty; + int i; + + if (!pl110_enabled(s)) + return; + + switch (s->ds->depth) { + case 0: + return; + case 8: + fntable = pl110_draw_fn_8; + dest_width = 1; + break; + case 15: + fntable = pl110_draw_fn_15; + dest_width = 2; + break; + case 16: + fntable = pl110_draw_fn_16; + dest_width = 2; + break; + case 24: + fntable = pl110_draw_fn_24; + dest_width = 3; + break; + case 32: + fntable = pl110_draw_fn_32; + dest_width = 4; + break; + default: + fprintf(stderr, "pl110: Bad color depth\n"); + exit(1); + } + if (s->cr & PL110_CR_BEBO) + fn = fntable[s->bpp + 6]; + else if (s->cr & PL110_CR_BEPO) + fn = fntable[s->bpp + 12]; + else + fn = fntable[s->bpp]; + + src_width = s->cols; + switch (s->bpp) { + case BPP_1: + src_width >>= 3; + break; + case BPP_2: + src_width >>= 2; + break; + case BPP_4: + src_width >>= 1; + break; + case BPP_8: + break; + case BPP_16: + src_width <<= 1; + break; + case BPP_32: + src_width <<= 2; + break; + } + dest_width *= s->cols; + pallette = s->pallette; + base = s->upbase; + /* HACK: Arm aliases physical memory at 0x80000000. */ + if (base > 0x80000000) + base -= 0x80000000; + src = phys_ram_base + base; + dest = s->ds->data; + first = -1; + addr = base; + + dirty = cpu_physical_memory_get_dirty(addr, VGA_DIRTY_FLAG); + for (i = 0; i < s->rows; i++) { + new_dirty = 0; + if ((addr & TARGET_PAGE_MASK) + src_width >= TARGET_PAGE_SIZE) { + uint32_t tmp; + for (tmp = 0; tmp < src_width; tmp += TARGET_PAGE_SIZE) { + new_dirty |= cpu_physical_memory_get_dirty(addr + tmp, + VGA_DIRTY_FLAG); + } + } + + if (dirty || new_dirty || s->invalidate) { + fn(pallette, dest, src, s->cols); + if (first == -1) + first = i; + last = i; + } + dirty = new_dirty; + addr += src_width; + dest += dest_width; + src += src_width; + } + if (first < 0) + return; + + s->invalidate = 0; + cpu_physical_memory_reset_dirty(base + first * src_width, + base + (last + 1) * src_width, + VGA_DIRTY_FLAG); + dpy_update(s->ds, 0, first, s->cols, last - first + 1); +} + +static void pl110_invalidate_display(void * opaque) +{ + pl110_state *s = (pl110_state *)opaque; + s->invalidate = 1; +} + +static void pl110_update_pallette(pl110_state *s, int n) +{ + int i; + uint32_t raw; + unsigned int r, g, b; + + raw = s->raw_pallette[n]; + n <<= 1; + for (i = 0; i < 2; i++) { + r = (raw & 0x1f) << 3; + raw >>= 5; + g = (raw & 0x1f) << 3; + raw >>= 5; + b = (raw & 0x1f) << 3; + /* The I bit is ignored. */ + raw >>= 6; + switch (s->ds->depth) { + case 8: + s->pallette[n] = rgb_to_pixel8(r, g, b); + break; + case 15: + s->pallette[n] = rgb_to_pixel15(r, g, b); + break; + case 16: + s->pallette[n] = rgb_to_pixel16(r, g, b); + break; + case 24: + case 32: + s->pallette[n] = rgb_to_pixel32(r, g, b); + break; + } + n++; + } +} + +static void pl110_resize(pl110_state *s, int width, int height) +{ + if (width != s->cols || height != s->rows) { + if (pl110_enabled(s)) { + dpy_resize(s->ds, width, height); + } + } + s->cols = width; + s->rows = height; +} + +/* Update interrupts. */ +static void pl110_update(pl110_state *s) +{ + /* TODO: Implement interrupts. */ +} + +static uint32_t pl110_read(void *opaque, target_phys_addr_t offset) +{ + pl110_state *s = (pl110_state *)opaque; + + offset -= s->base; + if (offset >= 0xfe0 && offset < 0x1000) { + if (s->versatile) + return pl110_versatile_id[(offset - 0xfe0) >> 2]; + else + return pl110_id[(offset - 0xfe0) >> 2]; + } + if (offset >= 0x200 && offset < 0x400) { + return s->raw_pallette[(offset - 0x200) >> 2]; + } + switch (offset >> 2) { + case 0: /* LCDTiming0 */ + return s->timing[0]; + case 1: /* LCDTiming1 */ + return s->timing[1]; + case 2: /* LCDTiming2 */ + return s->timing[2]; + case 3: /* LCDTiming3 */ + return s->timing[3]; + case 4: /* LCDUPBASE */ + return s->upbase; + case 5: /* LCDLPBASE */ + return s->lpbase; + case 6: /* LCDIMSC */ + return s->int_mask; + case 7: /* LCDControl */ + return s->cr; + case 8: /* LCDRIS */ + return s->int_status; + case 9: /* LCDMIS */ + return s->int_status & s->int_mask; + case 11: /* LCDUPCURR */ + /* TODO: Implement vertical refresh. */ + return s->upbase; + case 12: /* LCDLPCURR */ + return s->lpbase; + default: + cpu_abort (cpu_single_env, "pl110_read: Bad offset %x\n", offset); + return 0; + } +} + +static void pl110_write(void *opaque, target_phys_addr_t offset, + uint32_t val) +{ + pl110_state *s = (pl110_state *)opaque; + int n; + + /* For simplicity invalidate the display whenever a control register + is writen to. */ + s->invalidate = 1; + offset -= s->base; + if (offset >= 0x200 && offset < 0x400) { + /* Pallette. */ + n = (offset - 0x200) >> 2; + s->raw_pallette[(offset - 0x200) >> 2] = val; + pl110_update_pallette(s, n); + return; + } + switch (offset >> 2) { + case 0: /* LCDTiming0 */ + s->timing[0] = val; + n = ((val & 0xfc) + 4) * 4; + pl110_resize(s, n, s->rows); + break; + case 1: /* LCDTiming1 */ + s->timing[1] = val; + n = (val & 0x3ff) + 1; + pl110_resize(s, s->cols, n); + break; + case 2: /* LCDTiming2 */ + s->timing[2] = val; + break; + case 3: /* LCDTiming3 */ + s->timing[3] = val; + break; + case 4: /* LCDUPBASE */ + s->upbase = val; + break; + case 5: /* LCDLPBASE */ + s->lpbase = val; + break; + case 6: /* LCDIMSC */ + if (s->versatile) + goto control; + imsc: + s->int_mask = val; + pl110_update(s); + break; + case 7: /* LCDControl */ + if (s->versatile) + goto imsc; + control: + s->cr = val; + s->bpp = (val >> 1) & 7; + if (pl110_enabled(s)) { + dpy_resize(s->ds, s->cols, s->rows); + } + break; + case 10: /* LCDICR */ + s->int_status &= ~val; + pl110_update(s); + break; + default: + cpu_abort (cpu_single_env, "pl110_write: Bad offset %x\n", offset); + } +} + +static CPUReadMemoryFunc *pl110_readfn[] = { + pl110_read, + pl110_read, + pl110_read +}; + +static CPUWriteMemoryFunc *pl110_writefn[] = { + pl110_write, + pl110_write, + pl110_write +}; + +void *pl110_init(DisplayState *ds, uint32_t base, void *pic, int irq, + int versatile) +{ + pl110_state *s; + int iomemtype; + + s = (pl110_state *)qemu_mallocz(sizeof(pl110_state)); + iomemtype = cpu_register_io_memory(0, pl110_readfn, + pl110_writefn, s); + cpu_register_physical_memory(base, 0x00000fff, iomemtype); + s->base = base; + s->ds = ds; + s->versatile = versatile; + s->pic = pic; + s->irq = irq; + graphic_console_init(ds, pl110_update_display, pl110_invalidate_display, + NULL, s); + /* ??? Save/restore. */ + return s; +} diff --git a/tools/ioemu/hw/pl110_template.h b/tools/ioemu/hw/pl110_template.h new file mode 100644 index 0000000000..db05035b01 --- /dev/null +++ b/tools/ioemu/hw/pl110_template.h @@ -0,0 +1,252 @@ +/* + * Arm PrimeCell PL110 Color LCD Controller + * + * Copyright (c) 2005 CodeSourcery, LLC. + * Written by Paul Brook + * + * This code is licenced under the GNU LGPL + * + * Framebuffer format conversion routines. + */ + +#ifndef ORDER + +#if BITS == 8 +#define COPY_PIXEL(to, from) *(to++) = from +#elif BITS == 15 || BITS == 16 +#define COPY_PIXEL(to, from) *(uint16_t *)to = from; to += 2; +#elif BITS == 24 +#define COPY_PIXEL(to, from) \ + *(to++) = from; *(to++) = (from) >> 8; *(to++) = (from) >> 16 +#elif BITS == 32 +#define COPY_PIXEL(to, from) *(uint32_t *)to = from; to += 4; +#else +#error unknown bit depth +#endif + +#define ORDER 0 +#include "pl110_template.h" +#define ORDER 1 +#include "pl110_template.h" +#define ORDER 2 +#include "pl110_template.h" + +static drawfn glue(pl110_draw_fn_,BITS)[18] = +{ + glue(pl110_draw_line1_lblp,BITS), + glue(pl110_draw_line2_lblp,BITS), + glue(pl110_draw_line4_lblp,BITS), + glue(pl110_draw_line8_lblp,BITS), + glue(pl110_draw_line16_lblp,BITS), + glue(pl110_draw_line32_lblp,BITS), + + glue(pl110_draw_line1_bbbp,BITS), + glue(pl110_draw_line2_bbbp,BITS), + glue(pl110_draw_line4_bbbp,BITS), + glue(pl110_draw_line8_bbbp,BITS), + glue(pl110_draw_line16_bbbp,BITS), + glue(pl110_draw_line32_bbbp,BITS), + + glue(pl110_draw_line1_lbbp,BITS), + glue(pl110_draw_line2_lbbp,BITS), + glue(pl110_draw_line4_lbbp,BITS), + glue(pl110_draw_line8_lbbp,BITS), + glue(pl110_draw_line16_lbbp,BITS), + glue(pl110_draw_line32_lbbp,BITS) +}; + +#undef BITS +#undef COPY_PIXEL + +#else + +#if ORDER == 0 +#define NAME glue(lblp, BITS) +#ifdef WORDS_BIGENDIAN +#define SWAP_WORDS 1 +#endif +#elif ORDER == 1 +#define NAME glue(bbbp, BITS) +#ifndef WORDS_BIGENDIAN +#define SWAP_WORDS 1 +#endif +#else +#define SWAP_PIXELS 1 +#define NAME glue(lbbp, BITS) +#ifdef WORDS_BIGENDIAN +#define SWAP_WORDS 1 +#endif +#endif + +#define FN_2(x, y) FN(x, y) FN(x+1, y) +#define FN_4(x, y) FN_2(x, y) FN_2(x+1, y) +#define FN_8(y) FN_4(0, y) FN_4(4, y) + +static void glue(pl110_draw_line1_,NAME)(uint32_t *pallette, uint8_t *d, const uint8_t *src, int width) +{ + uint32_t data; + while (width > 0) { + data = *(uint32_t *)src; +#ifdef SWAP_PIXELS +#define FN(x, y) COPY_PIXEL(d, pallette[(data >> (y + 7 - (x))) & 1]); +#else +#define FN(x, y) COPY_PIXEL(d, pallette[(data >> ((x) + y)) & 1]); +#endif +#ifdef SWAP_WORDS + FN_8(24) + FN_8(16) + FN_8(8) + FN_8(0) +#else + FN_8(0) + FN_8(8) + FN_8(16) + FN_8(24) +#endif +#undef FN + width -= 32; + src += 4; + } +} + +static void glue(pl110_draw_line2_,NAME)(uint32_t *pallette, uint8_t *d, const uint8_t *src, int width) +{ + uint32_t data; + while (width > 0) { + data = *(uint32_t *)src; +#ifdef SWAP_PIXELS +#define FN(x, y) COPY_PIXEL(d, pallette[(data >> (y + 6 - (x)*2)) & 3]); +#else +#define FN(x, y) COPY_PIXEL(d, pallette[(data >> ((x)*2 + y)) & 3]); +#endif +#ifdef SWAP_WORDS + FN_4(0, 24) + FN_4(0, 16) + FN_4(0, 8) + FN_4(0, 0) +#else + FN_4(0, 0) + FN_4(0, 8) + FN_4(0, 16) + FN_4(0, 24) +#endif +#undef FN + width -= 16; + src += 4; + } +} + +static void glue(pl110_draw_line4_,NAME)(uint32_t *pallette, uint8_t *d, const uint8_t *src, int width) +{ + uint32_t data; + while (width > 0) { + data = *(uint32_t *)src; +#ifdef SWAP_PIXELS +#define FN(x, y) COPY_PIXEL(d, pallette[(data >> (y + 4 - (x)*4)) & 0xf]); +#else +#define FN(x, y) COPY_PIXEL(d, pallette[(data >> ((x)*4 + y)) & 0xf]); +#endif +#ifdef SWAP_WORDS + FN_2(0, 24) + FN_2(0, 16) + FN_2(0, 8) + FN_2(0, 0) +#else + FN_2(0, 0) + FN_2(0, 8) + FN_2(0, 16) + FN_2(0, 24) +#endif +#undef FN + width -= 8; + src += 4; + } +} + +static void glue(pl110_draw_line8_,NAME)(uint32_t *pallette, uint8_t *d, const uint8_t *src, int width) +{ + uint32_t data; + while (width > 0) { + data = *(uint32_t *)src; +#define FN(x) COPY_PIXEL(d, pallette[(data >> (x)) & 0xff]); +#ifdef SWAP_WORDS + FN(24) + FN(16) + FN(8) + FN(0) +#else + FN(0) + FN(8) + FN(16) + FN(24) +#endif +#undef FN + width -= 4; + src += 4; + } +} + +static void glue(pl110_draw_line16_,NAME)(uint32_t *pallette, uint8_t *d, const uint8_t *src, int width) +{ + uint32_t data; + unsigned int r, g, b; + while (width > 0) { + data = *(uint32_t *)src; +#ifdef SWAP_WORDS + data = bswap32(data); +#endif +#if 0 + r = data & 0x1f; + data >>= 5; + g = data & 0x3f; + data >>= 6; + b = data & 0x1f; + data >>= 5; +#else + r = (data & 0x1f) << 3; + data >>= 5; + g = (data & 0x3f) << 2; + data >>= 6; + b = (data & 0x1f) << 3; + data >>= 5; +#endif + COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b)); + r = (data & 0x1f) << 3; + data >>= 5; + g = (data & 0x3f) << 2; + data >>= 6; + b = (data & 0x1f) << 3; + data >>= 5; + COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b)); + width -= 2; + src += 4; + } +} + +static void glue(pl110_draw_line32_,NAME)(uint32_t *pallette, uint8_t *d, const uint8_t *src, int width) +{ + uint32_t data; + unsigned int r, g, b; + while (width > 0) { + data = *(uint32_t *)src; +#ifdef SWAP_WORDS + r = data & 0xff; + g = (data >> 8) & 0xff; + b = (data >> 16) & 0xff; +#else + r = (data >> 24) & 0xff; + g = (data >> 16) & 0xff; + b = (data >> 8) & 0xff; +#endif + COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b)); + width--; + src += 4; + } +} + +#undef SWAP_PIXELS +#undef NAME +#undef SWAP_WORDS +#undef ORDER + +#endif diff --git a/tools/ioemu/hw/pl190.c b/tools/ioemu/hw/pl190.c new file mode 100644 index 0000000000..55c7180f5a --- /dev/null +++ b/tools/ioemu/hw/pl190.c @@ -0,0 +1,252 @@ +/* + * Arm PrimeCell PL190 Vector Interrupt Controller + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GPL. + */ + +#include "vl.h" +#include "arm_pic.h" + +/* The number of virtual priority levels. 16 user vectors plus the + unvectored IRQ. Chained interrupts would require an additional level + if implemented. */ + +#define PL190_NUM_PRIO 17 + +typedef struct { + arm_pic_handler handler; + uint32_t base; + DisplayState *ds; + uint32_t level; + uint32_t soft_level; + uint32_t irq_enable; + uint32_t fiq_select; + uint32_t default_addr; + uint8_t vect_control[16]; + uint32_t vect_addr[PL190_NUM_PRIO]; + /* Mask containing interrupts with higher priority than this one. */ + uint32_t prio_mask[PL190_NUM_PRIO + 1]; + int protected; + /* Current priority level. */ + int priority; + int prev_prio[PL190_NUM_PRIO]; + void *parent; + int irq; + int fiq; +} pl190_state; + +static const unsigned char pl190_id[] = +{ 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 }; + +static inline uint32_t pl190_irq_level(pl190_state *s) +{ + return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select; +} + +/* Update interrupts. */ +static void pl190_update(pl190_state *s) +{ + uint32_t level = pl190_irq_level(s); + int set; + + set = (level & s->prio_mask[s->priority]) != 0; + pic_set_irq_new(s->parent, s->irq, set); + set = ((s->level | s->soft_level) & s->fiq_select) != 0; + pic_set_irq_new(s->parent, s->fiq, set); +} + +static void pl190_set_irq(void *opaque, int irq, int level) +{ + pl190_state *s = (pl190_state *)opaque; + + if (level) + s->level |= 1u << irq; + else + s->level &= ~(1u << irq); + pl190_update(s); +} + +static void pl190_update_vectors(pl190_state *s) +{ + uint32_t mask; + int i; + int n; + + mask = 0; + for (i = 0; i < 16; i++) + { + s->prio_mask[i] = mask; + if (s->vect_control[i] & 0x20) + { + n = s->vect_control[i] & 0x1f; + mask |= 1 << n; + } + } + s->prio_mask[16] = mask; + pl190_update(s); +} + +static uint32_t pl190_read(void *opaque, target_phys_addr_t offset) +{ + pl190_state *s = (pl190_state *)opaque; + int i; + + offset -= s->base; + if (offset >= 0xfe0 && offset < 0x1000) { + return pl190_id[(offset - 0xfe0) >> 2]; + } + if (offset >= 0x100 && offset < 0x140) { + return s->vect_addr[(offset - 0x100) >> 2]; + } + if (offset >= 0x200 && offset < 0x240) { + return s->vect_control[(offset - 0x200) >> 2]; + } + switch (offset >> 2) { + case 0: /* IRQSTATUS */ + return pl190_irq_level(s); + case 1: /* FIQSATUS */ + return (s->level | s->soft_level) & s->fiq_select; + case 2: /* RAWINTR */ + return s->level | s->soft_level; + case 3: /* INTSELECT */ + return s->fiq_select; + case 4: /* INTENABLE */ + return s->irq_enable; + case 6: /* SOFTINT */ + return s->soft_level; + case 8: /* PROTECTION */ + return s->protected; + case 12: /* VECTADDR */ + /* Read vector address at the start of an ISR. Increases the + current priority level to that of the current interrupt. */ + for (i = 0; i < s->priority; i++) + { + if ((s->level | s->soft_level) & s->prio_mask[i]) + break; + } + /* Reading this value with no pending interrupts is undefined. + We return the default address. */ + if (i == PL190_NUM_PRIO) + return s->vect_addr[16]; + if (i < s->priority) + { + s->prev_prio[i] = s->priority; + s->priority = i; + pl190_update(s); + } + return s->vect_addr[s->priority]; + case 13: /* DEFVECTADDR */ + return s->vect_addr[16]; + default: + cpu_abort (cpu_single_env, "pl190_read: Bad offset %x\n", offset); + return 0; + } +} + +static void pl190_write(void *opaque, target_phys_addr_t offset, uint32_t val) +{ + pl190_state *s = (pl190_state *)opaque; + + offset -= s->base; + if (offset >= 0x100 && offset < 0x140) { + s->vect_addr[(offset - 0x100) >> 2] = val; + pl190_update_vectors(s); + return; + } + if (offset >= 0x200 && offset < 0x240) { + s->vect_control[(offset - 0x200) >> 2] = val; + pl190_update_vectors(s); + return; + } + switch (offset >> 2) { + case 0: /* SELECT */ + /* This is a readonly register, but linux tries to write to it + anyway. Ignore the write. */ + break; + case 3: /* INTSELECT */ + s->fiq_select = val; + break; + case 4: /* INTENABLE */ + s->irq_enable |= val; + break; + case 5: /* INTENCLEAR */ + s->irq_enable &= ~val; + break; + case 6: /* SOFTINT */ + s->soft_level |= val; + break; + case 7: /* SOFTINTCLEAR */ + s->soft_level &= ~val; + break; + case 8: /* PROTECTION */ + /* TODO: Protection (supervisor only access) is not implemented. */ + s->protected = val & 1; + break; + case 12: /* VECTADDR */ + /* Restore the previous priority level. The value written is + ignored. */ + if (s->priority < PL190_NUM_PRIO) + s->priority = s->prev_prio[s->priority]; + break; + case 13: /* DEFVECTADDR */ + s->default_addr = val; + break; + case 0xc0: /* ITCR */ + if (val) + cpu_abort(cpu_single_env, "pl190: Test mode not implemented\n"); + break; + default: + cpu_abort(cpu_single_env, "pl190_write: Bad offset %x\n", offset); + return; + } + pl190_update(s); +} + +static CPUReadMemoryFunc *pl190_readfn[] = { + pl190_read, + pl190_read, + pl190_read +}; + +static CPUWriteMemoryFunc *pl190_writefn[] = { + pl190_write, + pl190_write, + pl190_write +}; + +void pl190_reset(pl190_state *s) +{ + int i; + + for (i = 0; i < 16; i++) + { + s->vect_addr[i] = 0; + s->vect_control[i] = 0; + } + s->vect_addr[16] = 0; + s->prio_mask[17] = 0xffffffff; + s->priority = PL190_NUM_PRIO; + pl190_update_vectors(s); +} + +void *pl190_init(uint32_t base, void *parent, int irq, int fiq) +{ + pl190_state *s; + int iomemtype; + + s = (pl190_state *)qemu_mallocz(sizeof(pl190_state)); + iomemtype = cpu_register_io_memory(0, pl190_readfn, + pl190_writefn, s); + cpu_register_physical_memory(base, 0x00000fff, iomemtype); + s->handler = pl190_set_irq; + s->base = base; + s->parent = parent; + s->irq = irq; + s->fiq = fiq; + pl190_reset(s); + /* ??? Save/restore. */ + return s; +} diff --git a/tools/ioemu/hw/ppc.c b/tools/ioemu/hw/ppc.c new file mode 100644 index 0000000000..3743ad7861 --- /dev/null +++ b/tools/ioemu/hw/ppc.c @@ -0,0 +1,428 @@ +/* + * QEMU generic PPC hardware System Emulator + * + * Copyright (c) 2003-2004 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "m48t59.h" + +/*****************************************************************************/ +/* PPC time base and decrementer emulation */ +//#define DEBUG_TB + +struct ppc_tb_t { + /* Time base management */ + int64_t tb_offset; /* Compensation */ + uint32_t tb_freq; /* TB frequency */ + /* Decrementer management */ + uint64_t decr_next; /* Tick for next decr interrupt */ + struct QEMUTimer *decr_timer; +}; + +static inline uint64_t cpu_ppc_get_tb (ppc_tb_t *tb_env) +{ + /* TB time in tb periods */ + return muldiv64(qemu_get_clock(vm_clock) + tb_env->tb_offset, + tb_env->tb_freq, ticks_per_sec); +} + +uint32_t cpu_ppc_load_tbl (CPUState *env) +{ + ppc_tb_t *tb_env = env->tb_env; + uint64_t tb; + + tb = cpu_ppc_get_tb(tb_env); +#ifdef DEBUG_TB + { + static int last_time; + int now; + now = time(NULL); + if (last_time != now) { + last_time = now; + printf("%s: tb=0x%016lx %d %08lx\n", + __func__, tb, now, tb_env->tb_offset); + } + } +#endif + + return tb & 0xFFFFFFFF; +} + +uint32_t cpu_ppc_load_tbu (CPUState *env) +{ + ppc_tb_t *tb_env = env->tb_env; + uint64_t tb; + + tb = cpu_ppc_get_tb(tb_env); +#ifdef DEBUG_TB + printf("%s: tb=0x%016lx\n", __func__, tb); +#endif + return tb >> 32; +} + +static void cpu_ppc_store_tb (ppc_tb_t *tb_env, uint64_t value) +{ + tb_env->tb_offset = muldiv64(value, ticks_per_sec, tb_env->tb_freq) + - qemu_get_clock(vm_clock); +#ifdef DEBUG_TB + printf("%s: tb=0x%016lx offset=%08x\n", __func__, value); +#endif +} + +void cpu_ppc_store_tbu (CPUState *env, uint32_t value) +{ + ppc_tb_t *tb_env = env->tb_env; + + cpu_ppc_store_tb(tb_env, + ((uint64_t)value << 32) | cpu_ppc_load_tbl(env)); +} + +void cpu_ppc_store_tbl (CPUState *env, uint32_t value) +{ + ppc_tb_t *tb_env = env->tb_env; + + cpu_ppc_store_tb(tb_env, + ((uint64_t)cpu_ppc_load_tbu(env) << 32) | value); +} + +uint32_t cpu_ppc_load_decr (CPUState *env) +{ + ppc_tb_t *tb_env = env->tb_env; + uint32_t decr; + int64_t diff; + + diff = tb_env->decr_next - qemu_get_clock(vm_clock); + if (diff >= 0) + decr = muldiv64(diff, tb_env->tb_freq, ticks_per_sec); + else + decr = -muldiv64(-diff, tb_env->tb_freq, ticks_per_sec); +#if defined(DEBUG_TB) + printf("%s: 0x%08x\n", __func__, decr); +#endif + return decr; +} + +/* When decrementer expires, + * all we need to do is generate or queue a CPU exception + */ +static inline void cpu_ppc_decr_excp (CPUState *env) +{ + /* Raise it */ +#ifdef DEBUG_TB + printf("raise decrementer exception\n"); +#endif + cpu_interrupt(env, CPU_INTERRUPT_TIMER); +} + +static void _cpu_ppc_store_decr (CPUState *env, uint32_t decr, + uint32_t value, int is_excp) +{ + ppc_tb_t *tb_env = env->tb_env; + uint64_t now, next; + +#ifdef DEBUG_TB + printf("%s: 0x%08x => 0x%08x\n", __func__, decr, value); +#endif + now = qemu_get_clock(vm_clock); + next = now + muldiv64(value, ticks_per_sec, tb_env->tb_freq); + if (is_excp) + next += tb_env->decr_next - now; + if (next == now) + next++; + tb_env->decr_next = next; + /* Adjust timer */ + qemu_mod_timer(tb_env->decr_timer, next); + /* If we set a negative value and the decrementer was positive, + * raise an exception. + */ + if ((value & 0x80000000) && !(decr & 0x80000000)) + cpu_ppc_decr_excp(env); +} + +void cpu_ppc_store_decr (CPUState *env, uint32_t value) +{ + _cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0); +} + +static void cpu_ppc_decr_cb (void *opaque) +{ + _cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1); +} + +/* Set up (once) timebase frequency (in Hz) */ +ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq) +{ + ppc_tb_t *tb_env; + + tb_env = qemu_mallocz(sizeof(ppc_tb_t)); + if (tb_env == NULL) + return NULL; + env->tb_env = tb_env; + if (tb_env->tb_freq == 0 || 1) { + tb_env->tb_freq = freq; + /* Create new timer */ + tb_env->decr_timer = + qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env); + /* There is a bug in 2.4 kernels: + * if a decrementer exception is pending when it enables msr_ee, + * it's not ready to handle it... + */ + _cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0); + } + + return tb_env; +} + +#if 0 +/*****************************************************************************/ +/* Handle system reset (for now, just stop emulation) */ +void cpu_ppc_reset (CPUState *env) +{ + printf("Reset asked... Stop emulation\n"); + abort(); +} +#endif + +static void PPC_io_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + cpu_outb(NULL, addr & 0xffff, value); +} + +static uint32_t PPC_io_readb (void *opaque, target_phys_addr_t addr) +{ + uint32_t ret = cpu_inb(NULL, addr & 0xffff); + return ret; +} + +static void PPC_io_writew (void *opaque, target_phys_addr_t addr, uint32_t value) +{ +#ifdef TARGET_WORDS_BIGENDIAN + value = bswap16(value); +#endif + cpu_outw(NULL, addr & 0xffff, value); +} + +static uint32_t PPC_io_readw (void *opaque, target_phys_addr_t addr) +{ + uint32_t ret = cpu_inw(NULL, addr & 0xffff); +#ifdef TARGET_WORDS_BIGENDIAN + ret = bswap16(ret); +#endif + return ret; +} + +static void PPC_io_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ +#ifdef TARGET_WORDS_BIGENDIAN + value = bswap32(value); +#endif + cpu_outl(NULL, addr & 0xffff, value); +} + +static uint32_t PPC_io_readl (void *opaque, target_phys_addr_t addr) +{ + uint32_t ret = cpu_inl(NULL, addr & 0xffff); + +#ifdef TARGET_WORDS_BIGENDIAN + ret = bswap32(ret); +#endif + return ret; +} + +CPUWriteMemoryFunc *PPC_io_write[] = { + &PPC_io_writeb, + &PPC_io_writew, + &PPC_io_writel, +}; + +CPUReadMemoryFunc *PPC_io_read[] = { + &PPC_io_readb, + &PPC_io_readw, + &PPC_io_readl, +}; + +/*****************************************************************************/ +/* Debug port */ +void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val) +{ + addr &= 0xF; + switch (addr) { + case 0: + printf("%c", val); + break; + case 1: + printf("\n"); + fflush(stdout); + break; + case 2: + printf("Set loglevel to %04x\n", val); + cpu_set_log(val | 0x100); + break; + } +} + +/*****************************************************************************/ +/* NVRAM helpers */ +void NVRAM_set_byte (m48t59_t *nvram, uint32_t addr, uint8_t value) +{ + m48t59_write(nvram, addr, value); +} + +uint8_t NVRAM_get_byte (m48t59_t *nvram, uint32_t addr) +{ + return m48t59_read(nvram, addr); +} + +void NVRAM_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value) +{ + m48t59_write(nvram, addr, value >> 8); + m48t59_write(nvram, addr + 1, value & 0xFF); +} + +uint16_t NVRAM_get_word (m48t59_t *nvram, uint32_t addr) +{ + uint16_t tmp; + + tmp = m48t59_read(nvram, addr) << 8; + tmp |= m48t59_read(nvram, addr + 1); + return tmp; +} + +void NVRAM_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value) +{ + m48t59_write(nvram, addr, value >> 24); + m48t59_write(nvram, addr + 1, (value >> 16) & 0xFF); + m48t59_write(nvram, addr + 2, (value >> 8) & 0xFF); + m48t59_write(nvram, addr + 3, value & 0xFF); +} + +uint32_t NVRAM_get_lword (m48t59_t *nvram, uint32_t addr) +{ + uint32_t tmp; + + tmp = m48t59_read(nvram, addr) << 24; + tmp |= m48t59_read(nvram, addr + 1) << 16; + tmp |= m48t59_read(nvram, addr + 2) << 8; + tmp |= m48t59_read(nvram, addr + 3); + return tmp; +} + +void NVRAM_set_string (m48t59_t *nvram, uint32_t addr, + const unsigned char *str, uint32_t max) +{ + int i; + + for (i = 0; i < max && str[i] != '\0'; i++) { + m48t59_write(nvram, addr + i, str[i]); + } + m48t59_write(nvram, addr + max - 1, '\0'); +} + +int NVRAM_get_string (m48t59_t *nvram, uint8_t *dst, uint16_t addr, int max) +{ + int i; + + memset(dst, 0, max); + for (i = 0; i < max; i++) { + dst[i] = NVRAM_get_byte(nvram, addr + i); + if (dst[i] == '\0') + break; + } + + return i; +} + +static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value) +{ + uint16_t tmp; + uint16_t pd, pd1, pd2; + + tmp = prev >> 8; + pd = prev ^ value; + pd1 = pd & 0x000F; + pd2 = ((pd >> 4) & 0x000F) ^ pd1; + tmp ^= (pd1 << 3) | (pd1 << 8); + tmp ^= pd2 | (pd2 << 7) | (pd2 << 12); + + return tmp; +} + +uint16_t NVRAM_compute_crc (m48t59_t *nvram, uint32_t start, uint32_t count) +{ + uint32_t i; + uint16_t crc = 0xFFFF; + int odd; + + odd = count & 1; + count &= ~1; + for (i = 0; i != count; i++) { + crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i)); + } + if (odd) { + crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8); + } + + return crc; +} + +#define CMDLINE_ADDR 0x017ff000 + +int PPC_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size, + const unsigned char *arch, + uint32_t RAM_size, int boot_device, + uint32_t kernel_image, uint32_t kernel_size, + const char *cmdline, + uint32_t initrd_image, uint32_t initrd_size, + uint32_t NVRAM_image, + int width, int height, int depth) +{ + uint16_t crc; + + /* Set parameters for Open Hack'Ware BIOS */ + NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16); + NVRAM_set_lword(nvram, 0x10, 0x00000002); /* structure v2 */ + NVRAM_set_word(nvram, 0x14, NVRAM_size); + NVRAM_set_string(nvram, 0x20, arch, 16); + NVRAM_set_lword(nvram, 0x30, RAM_size); + NVRAM_set_byte(nvram, 0x34, boot_device); + NVRAM_set_lword(nvram, 0x38, kernel_image); + NVRAM_set_lword(nvram, 0x3C, kernel_size); + if (cmdline) { + /* XXX: put the cmdline in NVRAM too ? */ + strcpy(phys_ram_base + CMDLINE_ADDR, cmdline); + NVRAM_set_lword(nvram, 0x40, CMDLINE_ADDR); + NVRAM_set_lword(nvram, 0x44, strlen(cmdline)); + } else { + NVRAM_set_lword(nvram, 0x40, 0); + NVRAM_set_lword(nvram, 0x44, 0); + } + NVRAM_set_lword(nvram, 0x48, initrd_image); + NVRAM_set_lword(nvram, 0x4C, initrd_size); + NVRAM_set_lword(nvram, 0x50, NVRAM_image); + + NVRAM_set_word(nvram, 0x54, width); + NVRAM_set_word(nvram, 0x56, height); + NVRAM_set_word(nvram, 0x58, depth); + crc = NVRAM_compute_crc(nvram, 0x00, 0xF8); + NVRAM_set_word(nvram, 0xFC, crc); + + return 0; +} diff --git a/tools/ioemu/hw/ppc_chrp.c b/tools/ioemu/hw/ppc_chrp.c new file mode 100644 index 0000000000..33167cdf75 --- /dev/null +++ b/tools/ioemu/hw/ppc_chrp.c @@ -0,0 +1,564 @@ +/* + * QEMU PPC CHRP/PMAC hardware System Emulator + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#define BIOS_FILENAME "ppc_rom.bin" +#define VGABIOS_FILENAME "video.x" +#define NVRAM_SIZE 0x2000 + +#define KERNEL_LOAD_ADDR 0x01000000 +#define INITRD_LOAD_ADDR 0x01800000 + +/* MacIO devices (mapped inside the MacIO address space): CUDA, DBDMA, + NVRAM */ + +static int dbdma_mem_index; +static int cuda_mem_index; +static int ide0_mem_index = -1; +static int ide1_mem_index = -1; +static int openpic_mem_index = -1; +static int heathrow_pic_mem_index = -1; +static int macio_nvram_mem_index = -1; + +/* DBDMA: currently no op - should suffice right now */ + +static void dbdma_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + printf("%s: 0x%08x <= 0x%08x\n", __func__, addr, value); +} + +static void dbdma_writew (void *opaque, target_phys_addr_t addr, uint32_t value) +{ +} + +static void dbdma_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ +} + +static uint32_t dbdma_readb (void *opaque, target_phys_addr_t addr) +{ + printf("%s: 0x%08x => 0x00000000\n", __func__, addr); + return 0; +} + +static uint32_t dbdma_readw (void *opaque, target_phys_addr_t addr) +{ + return 0; +} + +static uint32_t dbdma_readl (void *opaque, target_phys_addr_t addr) +{ + return 0; +} + +static CPUWriteMemoryFunc *dbdma_write[] = { + &dbdma_writeb, + &dbdma_writew, + &dbdma_writel, +}; + +static CPUReadMemoryFunc *dbdma_read[] = { + &dbdma_readb, + &dbdma_readw, + &dbdma_readl, +}; + +/* macio style NVRAM device */ +typedef struct MacIONVRAMState { + uint8_t data[0x2000]; +} MacIONVRAMState; + +static void macio_nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + MacIONVRAMState *s = opaque; + addr = (addr >> 4) & 0x1fff; + s->data[addr] = value; + // printf("macio_nvram_writeb %04x = %02x\n", addr, value); +} + +static uint32_t macio_nvram_readb (void *opaque, target_phys_addr_t addr) +{ + MacIONVRAMState *s = opaque; + uint32_t value; + + addr = (addr >> 4) & 0x1fff; + value = s->data[addr]; + // printf("macio_nvram_readb %04x = %02x\n", addr, value); + return value; +} + +static CPUWriteMemoryFunc *macio_nvram_write[] = { + &macio_nvram_writeb, + &macio_nvram_writeb, + &macio_nvram_writeb, +}; + +static CPUReadMemoryFunc *macio_nvram_read[] = { + &macio_nvram_readb, + &macio_nvram_readb, + &macio_nvram_readb, +}; + +static MacIONVRAMState *macio_nvram_init(void) +{ + MacIONVRAMState *s; + s = qemu_mallocz(sizeof(MacIONVRAMState)); + if (!s) + return NULL; + macio_nvram_mem_index = cpu_register_io_memory(0, macio_nvram_read, + macio_nvram_write, s); + return s; +} + +static void macio_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + if (heathrow_pic_mem_index >= 0) { + cpu_register_physical_memory(addr + 0x00000, 0x1000, + heathrow_pic_mem_index); + } + cpu_register_physical_memory(addr + 0x08000, 0x1000, dbdma_mem_index); + cpu_register_physical_memory(addr + 0x16000, 0x2000, cuda_mem_index); + if (ide0_mem_index >= 0) + cpu_register_physical_memory(addr + 0x1f000, 0x1000, ide0_mem_index); + if (ide1_mem_index >= 0) + cpu_register_physical_memory(addr + 0x20000, 0x1000, ide1_mem_index); + if (openpic_mem_index >= 0) { + cpu_register_physical_memory(addr + 0x40000, 0x40000, + openpic_mem_index); + } + if (macio_nvram_mem_index >= 0) + cpu_register_physical_memory(addr + 0x60000, 0x20000, macio_nvram_mem_index); +} + +static void macio_init(PCIBus *bus, int device_id) +{ + PCIDevice *d; + + d = pci_register_device(bus, "macio", sizeof(PCIDevice), + -1, NULL, NULL); + /* Note: this code is strongly inspirated from the corresponding code + in PearPC */ + d->config[0x00] = 0x6b; // vendor_id + d->config[0x01] = 0x10; + d->config[0x02] = device_id; + d->config[0x03] = device_id >> 8; + + d->config[0x0a] = 0x00; // class_sub = pci2pci + d->config[0x0b] = 0xff; // class_base = bridge + d->config[0x0e] = 0x00; // header_type + + d->config[0x3d] = 0x01; // interrupt on pin 1 + + dbdma_mem_index = cpu_register_io_memory(0, dbdma_read, dbdma_write, NULL); + + pci_register_io_region(d, 0, 0x80000, + PCI_ADDRESS_SPACE_MEM, macio_map); +} + +/* UniN device */ +static void unin_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ +} + +static uint32_t unin_readl (void *opaque, target_phys_addr_t addr) +{ + return 0; +} + +static CPUWriteMemoryFunc *unin_write[] = { + &unin_writel, + &unin_writel, + &unin_writel, +}; + +static CPUReadMemoryFunc *unin_read[] = { + &unin_readl, + &unin_readl, + &unin_readl, +}; + +/* temporary frame buffer OSI calls for the video.x driver. The right + solution is to modify the driver to use VGA PCI I/Os */ +static int vga_osi_call(CPUState *env) +{ + static int vga_vbl_enabled; + int linesize; + + // printf("osi_call R5=%d\n", env->gpr[5]); + + /* same handler as PearPC, coming from the original MOL video + driver. */ + switch(env->gpr[5]) { + case 4: + break; + case 28: /* set_vmode */ + if (env->gpr[6] != 1 || env->gpr[7] != 0) + env->gpr[3] = 1; + else + env->gpr[3] = 0; + break; + case 29: /* get_vmode_info */ + if (env->gpr[6] != 0) { + if (env->gpr[6] != 1 || env->gpr[7] != 0) { + env->gpr[3] = 1; + break; + } + } + env->gpr[3] = 0; + env->gpr[4] = (1 << 16) | 1; /* num_vmodes, cur_vmode */ + env->gpr[5] = (1 << 16) | 0; /* num_depths, cur_depth_mode */ + env->gpr[6] = (graphic_width << 16) | graphic_height; /* w, h */ + env->gpr[7] = 85 << 16; /* refresh rate */ + env->gpr[8] = (graphic_depth + 7) & ~7; /* depth (round to byte) */ + linesize = ((graphic_depth + 7) >> 3) * graphic_width; + linesize = (linesize + 3) & ~3; + env->gpr[9] = (linesize << 16) | 0; /* row_bytes, offset */ + break; + case 31: /* set_video power */ + env->gpr[3] = 0; + break; + case 39: /* video_ctrl */ + if (env->gpr[6] == 0 || env->gpr[6] == 1) + vga_vbl_enabled = env->gpr[6]; + env->gpr[3] = 0; + break; + case 47: + break; + case 59: /* set_color */ + /* R6 = index, R7 = RGB */ + env->gpr[3] = 0; + break; + case 64: /* get color */ + /* R6 = index */ + env->gpr[3] = 0; + break; + case 116: /* set hwcursor */ + /* R6 = x, R7 = y, R8 = visible, R9 = data */ + break; + default: + fprintf(stderr, "unsupported OSI call R5=%08x\n", env->gpr[5]); + break; + } + return 1; /* osi_call handled */ +} + +/* XXX: suppress that */ +static void pic_irq_request(void *opaque, int level) +{ +} + +static uint8_t nvram_chksum(const uint8_t *buf, int n) +{ + int sum, i; + sum = 0; + for(i = 0; i < n; i++) + sum += buf[i]; + return (sum & 0xff) + (sum >> 8); +} + +/* set a free Mac OS NVRAM partition */ +void pmac_format_nvram_partition(uint8_t *buf, int len) +{ + char partition_name[12] = "wwwwwwwwwwww"; + + buf[0] = 0x7f; /* free partition magic */ + buf[1] = 0; /* checksum */ + buf[2] = len >> 8; + buf[3] = len; + memcpy(buf + 4, partition_name, 12); + buf[1] = nvram_chksum(buf, 16); +} + +/* PowerPC CHRP hardware initialisation */ +static void ppc_chrp_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, + int snapshot, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + int is_heathrow) +{ + CPUState *env; + char buf[1024]; + SetIRQFunc *set_irq; + void *pic; + m48t59_t *nvram; + int PPC_io_memory, unin_memory; + int linux_boot, i; + unsigned long bios_offset, vga_bios_offset; + uint32_t kernel_base, kernel_size, initrd_base, initrd_size; + ppc_def_t *def; + PCIBus *pci_bus; + const char *arch_name; + int vga_bios_size, bios_size; + + linux_boot = (kernel_filename != NULL); + + /* init CPUs */ + env = cpu_init(); + register_savevm("cpu", 0, 3, cpu_save, cpu_load, env); + + /* Register CPU as a 74x/75x */ + /* XXX: CPU model (or PVR) should be provided on command line */ + // ppc_find_by_name("750gx", &def); // Linux boot OK + // ppc_find_by_name("750fx", &def); // Linux boot OK + /* Linux does not boot on 750cxe (and probably other 750cx based) + * because it assumes it has 8 IBAT & DBAT pairs as it only have 4. + */ + // ppc_find_by_name("750cxe", &def); + // ppc_find_by_name("750p", &def); + // ppc_find_by_name("740p", &def); + ppc_find_by_name("750", &def); + // ppc_find_by_name("740", &def); + // ppc_find_by_name("G3", &def); + // ppc_find_by_name("604r", &def); + // ppc_find_by_name("604e", &def); + // ppc_find_by_name("604", &def); + if (def == NULL) { + cpu_abort(env, "Unable to find PowerPC CPU definition\n"); + } + cpu_ppc_register(env, def); + + /* Set time-base frequency to 100 Mhz */ + cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); + + env->osi_call = vga_osi_call; + + /* allocate RAM */ + cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); + + /* allocate and load BIOS */ + bios_offset = ram_size + vga_ram_size; + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME); + bios_size = load_image(buf, phys_ram_base + bios_offset); + if (bios_size < 0 || bios_size > BIOS_SIZE) { + fprintf(stderr, "qemu: could not load PowerPC bios '%s'\n", buf); + exit(1); + } + bios_size = (bios_size + 0xfff) & ~0xfff; + cpu_register_physical_memory((uint32_t)(-bios_size), + bios_size, bios_offset | IO_MEM_ROM); + + /* allocate and load VGA BIOS */ + vga_bios_offset = bios_offset + bios_size; + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_FILENAME); + vga_bios_size = load_image(buf, phys_ram_base + vga_bios_offset + 8); + if (vga_bios_size < 0) { + /* if no bios is present, we can still work */ + fprintf(stderr, "qemu: warning: could not load VGA bios '%s'\n", buf); + vga_bios_size = 0; + } else { + /* set a specific header (XXX: find real Apple format for NDRV + drivers) */ + phys_ram_base[vga_bios_offset] = 'N'; + phys_ram_base[vga_bios_offset + 1] = 'D'; + phys_ram_base[vga_bios_offset + 2] = 'R'; + phys_ram_base[vga_bios_offset + 3] = 'V'; + cpu_to_be32w((uint32_t *)(phys_ram_base + vga_bios_offset + 4), + vga_bios_size); + vga_bios_size += 8; + } + vga_bios_size = (vga_bios_size + 0xfff) & ~0xfff; + + if (linux_boot) { + kernel_base = KERNEL_LOAD_ADDR; + /* now we can load the kernel */ + kernel_size = load_image(kernel_filename, phys_ram_base + kernel_base); + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + /* load initrd */ + if (initrd_filename) { + initrd_base = INITRD_LOAD_ADDR; + initrd_size = load_image(initrd_filename, + phys_ram_base + initrd_base); + if (initrd_size < 0) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + } else { + initrd_base = 0; + initrd_size = 0; + } + boot_device = 'm'; + } else { + kernel_base = 0; + kernel_size = 0; + initrd_base = 0; + initrd_size = 0; + } + + if (is_heathrow) { + isa_mem_base = 0x80000000; + pci_bus = pci_grackle_init(0xfec00000); + + /* Register 2 MB of ISA IO space */ + PPC_io_memory = cpu_register_io_memory(0, PPC_io_read, PPC_io_write, NULL); + cpu_register_physical_memory(0xfe000000, 0x00200000, PPC_io_memory); + + /* init basic PC hardware */ + vga_initialize(pci_bus, ds, phys_ram_base + ram_size, + ram_size, vga_ram_size, + vga_bios_offset, vga_bios_size); + pic = heathrow_pic_init(&heathrow_pic_mem_index); + set_irq = heathrow_pic_set_irq; + pci_set_pic(pci_bus, set_irq, pic); + + /* XXX: suppress that */ + isa_pic = pic_init(pic_irq_request, NULL); + + /* XXX: use Mac Serial port */ + serial_init(&pic_set_irq_new, isa_pic, 0x3f8, 4, serial_hds[0]); + + for(i = 0; i < nb_nics; i++) { + if (!nd_table[i].model) + nd_table[i].model = "ne2k_pci"; + pci_nic_init(pci_bus, &nd_table[i]); + } + + pci_cmd646_ide_init(pci_bus, &bs_table[0], 0); + + /* cuda also initialize ADB */ + cuda_mem_index = cuda_init(set_irq, pic, 0x12); + + adb_kbd_init(&adb_bus); + adb_mouse_init(&adb_bus); + + { + MacIONVRAMState *nvr; + nvr = macio_nvram_init(); + pmac_format_nvram_partition(nvr->data, 0x2000); + } + + macio_init(pci_bus, 0x0017); + + nvram = m48t59_init(8, 0xFFF04000, 0x0074, NVRAM_SIZE, 59); + + arch_name = "HEATHROW"; + } else { + isa_mem_base = 0x80000000; + pci_bus = pci_pmac_init(); + + /* Register 8 MB of ISA IO space */ + PPC_io_memory = cpu_register_io_memory(0, PPC_io_read, PPC_io_write, NULL); + cpu_register_physical_memory(0xF2000000, 0x00800000, PPC_io_memory); + + /* UniN init */ + unin_memory = cpu_register_io_memory(0, unin_read, unin_write, NULL); + cpu_register_physical_memory(0xf8000000, 0x00001000, unin_memory); + + /* init basic PC hardware */ + vga_initialize(pci_bus, ds, phys_ram_base + ram_size, + ram_size, vga_ram_size, + vga_bios_offset, vga_bios_size); + pic = openpic_init(NULL, &openpic_mem_index, 1, &env); + set_irq = openpic_set_irq; + pci_set_pic(pci_bus, set_irq, pic); + + /* XXX: suppress that */ + isa_pic = pic_init(pic_irq_request, NULL); + + /* XXX: use Mac Serial port */ + serial_init(&pic_set_irq_new, isa_pic, 0x3f8, 4, serial_hds[0]); + + for(i = 0; i < nb_nics; i++) { + pci_ne2000_init(pci_bus, &nd_table[i]); + } + +#if 1 + ide0_mem_index = pmac_ide_init(&bs_table[0], set_irq, pic, 0x13); + ide1_mem_index = pmac_ide_init(&bs_table[2], set_irq, pic, 0x14); +#else + pci_cmd646_ide_init(pci_bus, &bs_table[0], 0); +#endif + /* cuda also initialize ADB */ + cuda_mem_index = cuda_init(set_irq, pic, 0x19); + + adb_kbd_init(&adb_bus); + adb_mouse_init(&adb_bus); + + macio_init(pci_bus, 0x0022); + + nvram = m48t59_init(8, 0xFFF04000, 0x0074, NVRAM_SIZE, 59); + + arch_name = "MAC99"; + } + + if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) + graphic_depth = 15; + + PPC_NVRAM_set_params(nvram, NVRAM_SIZE, arch_name, ram_size, boot_device, + kernel_base, kernel_size, + kernel_cmdline, + initrd_base, initrd_size, + /* XXX: need an option to load a NVRAM image */ + 0, + graphic_width, graphic_height, graphic_depth); + /* No PCI init: the BIOS will do it */ + + /* Special port to get debug messages from Open-Firmware */ + register_ioport_write(0x0F00, 4, 1, &PPC_debug_write, NULL); +} + +static void ppc_core99_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, + int snapshot, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename) +{ + ppc_chrp_init(ram_size, vga_ram_size, boot_device, + ds, fd_filename, snapshot, + kernel_filename, kernel_cmdline, + initrd_filename, 0); +} + +static void ppc_heathrow_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, + int snapshot, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename) +{ + ppc_chrp_init(ram_size, vga_ram_size, boot_device, + ds, fd_filename, snapshot, + kernel_filename, kernel_cmdline, + initrd_filename, 1); +} + +QEMUMachine core99_machine = { + "mac99", + "Mac99 based PowerMAC", + ppc_core99_init, +}; + +QEMUMachine heathrow_machine = { + "g3bw", + "Heathrow based PowerMAC", + ppc_heathrow_init, +}; diff --git a/tools/ioemu/hw/ppc_prep.c b/tools/ioemu/hw/ppc_prep.c new file mode 100644 index 0000000000..9df430775d --- /dev/null +++ b/tools/ioemu/hw/ppc_prep.c @@ -0,0 +1,690 @@ +/* + * QEMU PPC PREP hardware System Emulator + * + * Copyright (c) 2003-2004 Jocelyn Mayer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define HARD_DEBUG_PPC_IO +//#define DEBUG_PPC_IO + +#define BIOS_FILENAME "ppc_rom.bin" +#define KERNEL_LOAD_ADDR 0x01000000 +#define INITRD_LOAD_ADDR 0x01800000 + +extern int loglevel; +extern FILE *logfile; + +#if defined (HARD_DEBUG_PPC_IO) && !defined (DEBUG_PPC_IO) +#define DEBUG_PPC_IO +#endif + +#if defined (HARD_DEBUG_PPC_IO) +#define PPC_IO_DPRINTF(fmt, args...) \ +do { \ + if (loglevel & CPU_LOG_IOPORT) { \ + fprintf(logfile, "%s: " fmt, __func__ , ##args); \ + } else { \ + printf("%s : " fmt, __func__ , ##args); \ + } \ +} while (0) +#elif defined (DEBUG_PPC_IO) +#define PPC_IO_DPRINTF(fmt, args...) \ +do { \ + if (loglevel & CPU_LOG_IOPORT) { \ + fprintf(logfile, "%s: " fmt, __func__ , ##args); \ + } \ +} while (0) +#else +#define PPC_IO_DPRINTF(fmt, args...) do { } while (0) +#endif + +/* Constants for devices init */ +static const int ide_iobase[2] = { 0x1f0, 0x170 }; +static const int ide_iobase2[2] = { 0x3f6, 0x376 }; +static const int ide_irq[2] = { 13, 13 }; + +#define NE2000_NB_MAX 6 + +static uint32_t ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 }; +static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 }; + +//static PITState *pit; + +/* ISA IO ports bridge */ +#define PPC_IO_BASE 0x80000000 + +/* Speaker port 0x61 */ +int speaker_data_on; +int dummy_refresh_clock; + +static void speaker_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ +#if 0 + speaker_data_on = (val >> 1) & 1; + pit_set_gate(pit, 2, val & 1); +#endif +} + +static uint32_t speaker_ioport_read(void *opaque, uint32_t addr) +{ +#if 0 + int out; + out = pit_get_out(pit, 2, qemu_get_clock(vm_clock)); + dummy_refresh_clock ^= 1; + return (speaker_data_on << 1) | pit_get_gate(pit, 2) | (out << 5) | + (dummy_refresh_clock << 4); +#endif + return 0; +} + +static void pic_irq_request(void *opaque, int level) +{ + if (level) + cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD); + else + cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD); +} + +/* PCI intack register */ +/* Read-only register (?) */ +static void _PPC_intack_write (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + // printf("%s: 0x%08x => 0x%08x\n", __func__, addr, value); +} + +static inline uint32_t _PPC_intack_read (target_phys_addr_t addr) +{ + uint32_t retval = 0; + + if (addr == 0xBFFFFFF0) + retval = pic_intack_read(isa_pic); + // printf("%s: 0x%08x <= %d\n", __func__, addr, retval); + + return retval; +} + +static uint32_t PPC_intack_readb (void *opaque, target_phys_addr_t addr) +{ + return _PPC_intack_read(addr); +} + +static uint32_t PPC_intack_readw (void *opaque, target_phys_addr_t addr) +{ +#ifdef TARGET_WORDS_BIGENDIAN + return bswap16(_PPC_intack_read(addr)); +#else + return _PPC_intack_read(addr); +#endif +} + +static uint32_t PPC_intack_readl (void *opaque, target_phys_addr_t addr) +{ +#ifdef TARGET_WORDS_BIGENDIAN + return bswap32(_PPC_intack_read(addr)); +#else + return _PPC_intack_read(addr); +#endif +} + +static CPUWriteMemoryFunc *PPC_intack_write[] = { + &_PPC_intack_write, + &_PPC_intack_write, + &_PPC_intack_write, +}; + +static CPUReadMemoryFunc *PPC_intack_read[] = { + &PPC_intack_readb, + &PPC_intack_readw, + &PPC_intack_readl, +}; + +/* PowerPC control and status registers */ +#if 0 // Not used +static struct { + /* IDs */ + uint32_t veni_devi; + uint32_t revi; + /* Control and status */ + uint32_t gcsr; + uint32_t xcfr; + uint32_t ct32; + uint32_t mcsr; + /* General purpose registers */ + uint32_t gprg[6]; + /* Exceptions */ + uint32_t feen; + uint32_t fest; + uint32_t fema; + uint32_t fecl; + uint32_t eeen; + uint32_t eest; + uint32_t eecl; + uint32_t eeint; + uint32_t eemck0; + uint32_t eemck1; + /* Error diagnostic */ +} XCSR; + +static void PPC_XCSR_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + printf("%s: 0x%08lx => 0x%08x\n", __func__, (long)addr, value); +} + +static void PPC_XCSR_writew (void *opaque, target_phys_addr_t addr, uint32_t value) +{ +#ifdef TARGET_WORDS_BIGENDIAN + value = bswap16(value); +#endif + printf("%s: 0x%08lx => 0x%08x\n", __func__, (long)addr, value); +} + +static void PPC_XCSR_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ +#ifdef TARGET_WORDS_BIGENDIAN + value = bswap32(value); +#endif + printf("%s: 0x%08lx => 0x%08x\n", __func__, (long)addr, value); +} + +static uint32_t PPC_XCSR_readb (void *opaque, target_phys_addr_t addr) +{ + uint32_t retval = 0; + + printf("%s: 0x%08lx <= %d\n", __func__, (long)addr, retval); + + return retval; +} + +static uint32_t PPC_XCSR_readw (void *opaque, target_phys_addr_t addr) +{ + uint32_t retval = 0; + + printf("%s: 0x%08lx <= %d\n", __func__, (long)addr, retval); +#ifdef TARGET_WORDS_BIGENDIAN + retval = bswap16(retval); +#endif + + return retval; +} + +static uint32_t PPC_XCSR_readl (void *opaque, target_phys_addr_t addr) +{ + uint32_t retval = 0; + + printf("%s: 0x%08lx <= %d\n", __func__, (long)addr, retval); +#ifdef TARGET_WORDS_BIGENDIAN + retval = bswap32(retval); +#endif + + return retval; +} + +static CPUWriteMemoryFunc *PPC_XCSR_write[] = { + &PPC_XCSR_writeb, + &PPC_XCSR_writew, + &PPC_XCSR_writel, +}; + +static CPUReadMemoryFunc *PPC_XCSR_read[] = { + &PPC_XCSR_readb, + &PPC_XCSR_readw, + &PPC_XCSR_readl, +}; +#endif + +/* Fake super-io ports for PREP platform (Intel 82378ZB) */ +typedef struct sysctrl_t { + m48t59_t *nvram; + uint8_t state; + uint8_t syscontrol; + uint8_t fake_io[2]; + int contiguous_map; + int endian; +} sysctrl_t; + +enum { + STATE_HARDFILE = 0x01, +}; + +static sysctrl_t *sysctrl; + +static void PREP_io_write (void *opaque, uint32_t addr, uint32_t val) +{ + sysctrl_t *sysctrl = opaque; + + PPC_IO_DPRINTF("0x%08lx => 0x%08x\n", (long)addr - PPC_IO_BASE, val); + sysctrl->fake_io[addr - 0x0398] = val; +} + +static uint32_t PREP_io_read (void *opaque, uint32_t addr) +{ + sysctrl_t *sysctrl = opaque; + + PPC_IO_DPRINTF("0x%08lx <= 0x%08x\n", (long)addr - PPC_IO_BASE, + sysctrl->fake_io[addr - 0x0398]); + return sysctrl->fake_io[addr - 0x0398]; +} + +static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) +{ + sysctrl_t *sysctrl = opaque; + + PPC_IO_DPRINTF("0x%08lx => 0x%08x\n", (long)addr - PPC_IO_BASE, val); + switch (addr) { + case 0x0092: + /* Special port 92 */ + /* Check soft reset asked */ + if (val & 0x01) { + // cpu_interrupt(first_cpu, CPU_INTERRUPT_RESET); + } + /* Check LE mode */ + if (val & 0x02) { + sysctrl->endian = 1; + } else { + sysctrl->endian = 0; + } + break; + case 0x0800: + /* Motorola CPU configuration register : read-only */ + break; + case 0x0802: + /* Motorola base module feature register : read-only */ + break; + case 0x0803: + /* Motorola base module status register : read-only */ + break; + case 0x0808: + /* Hardfile light register */ + if (val & 1) + sysctrl->state |= STATE_HARDFILE; + else + sysctrl->state &= ~STATE_HARDFILE; + break; + case 0x0810: + /* Password protect 1 register */ + if (sysctrl->nvram != NULL) + m48t59_toggle_lock(sysctrl->nvram, 1); + break; + case 0x0812: + /* Password protect 2 register */ + if (sysctrl->nvram != NULL) + m48t59_toggle_lock(sysctrl->nvram, 2); + break; + case 0x0814: + /* L2 invalidate register */ + // tlb_flush(first_cpu, 1); + break; + case 0x081C: + /* system control register */ + sysctrl->syscontrol = val & 0x0F; + break; + case 0x0850: + /* I/O map type register */ + sysctrl->contiguous_map = val & 0x01; + break; + default: + printf("ERROR: unaffected IO port write: %04lx => %02x\n", + (long)addr, val); + break; + } +} + +static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr) +{ + sysctrl_t *sysctrl = opaque; + uint32_t retval = 0xFF; + + switch (addr) { + case 0x0092: + /* Special port 92 */ + retval = 0x00; + break; + case 0x0800: + /* Motorola CPU configuration register */ + retval = 0xEF; /* MPC750 */ + break; + case 0x0802: + /* Motorola Base module feature register */ + retval = 0xAD; /* No ESCC, PMC slot neither ethernet */ + break; + case 0x0803: + /* Motorola base module status register */ + retval = 0xE0; /* Standard MPC750 */ + break; + case 0x080C: + /* Equipment present register: + * no L2 cache + * no upgrade processor + * no cards in PCI slots + * SCSI fuse is bad + */ + retval = 0x3C; + break; + case 0x0810: + /* Motorola base module extended feature register */ + retval = 0x39; /* No USB, CF and PCI bridge. NVRAM present */ + break; + case 0x0814: + /* L2 invalidate: don't care */ + break; + case 0x0818: + /* Keylock */ + retval = 0x00; + break; + case 0x081C: + /* system control register + * 7 - 6 / 1 - 0: L2 cache enable + */ + retval = sysctrl->syscontrol; + break; + case 0x0823: + /* */ + retval = 0x03; /* no L2 cache */ + break; + case 0x0850: + /* I/O map type register */ + retval = sysctrl->contiguous_map; + break; + default: + printf("ERROR: unaffected IO port: %04lx read\n", (long)addr); + break; + } + PPC_IO_DPRINTF("0x%08lx <= 0x%08x\n", (long)addr - PPC_IO_BASE, retval); + + return retval; +} + +static inline target_phys_addr_t prep_IO_address (sysctrl_t *sysctrl, + target_phys_addr_t addr) +{ + if (sysctrl->contiguous_map == 0) { + /* 64 KB contiguous space for IOs */ + addr &= 0xFFFF; + } else { + /* 8 MB non-contiguous space for IOs */ + addr = (addr & 0x1F) | ((addr & 0x007FFF000) >> 7); + } + + return addr; +} + +static void PPC_prep_io_writeb (void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + sysctrl_t *sysctrl = opaque; + + addr = prep_IO_address(sysctrl, addr); + cpu_outb(NULL, addr, value); +} + +static uint32_t PPC_prep_io_readb (void *opaque, target_phys_addr_t addr) +{ + sysctrl_t *sysctrl = opaque; + uint32_t ret; + + addr = prep_IO_address(sysctrl, addr); + ret = cpu_inb(NULL, addr); + + return ret; +} + +static void PPC_prep_io_writew (void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + sysctrl_t *sysctrl = opaque; + + addr = prep_IO_address(sysctrl, addr); +#ifdef TARGET_WORDS_BIGENDIAN + value = bswap16(value); +#endif + PPC_IO_DPRINTF("0x%08lx => 0x%08x\n", (long)addr, value); + cpu_outw(NULL, addr, value); +} + +static uint32_t PPC_prep_io_readw (void *opaque, target_phys_addr_t addr) +{ + sysctrl_t *sysctrl = opaque; + uint32_t ret; + + addr = prep_IO_address(sysctrl, addr); + ret = cpu_inw(NULL, addr); +#ifdef TARGET_WORDS_BIGENDIAN + ret = bswap16(ret); +#endif + PPC_IO_DPRINTF("0x%08lx <= 0x%08x\n", (long)addr, ret); + + return ret; +} + +static void PPC_prep_io_writel (void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + sysctrl_t *sysctrl = opaque; + + addr = prep_IO_address(sysctrl, addr); +#ifdef TARGET_WORDS_BIGENDIAN + value = bswap32(value); +#endif + PPC_IO_DPRINTF("0x%08lx => 0x%08x\n", (long)addr, value); + cpu_outl(NULL, addr, value); +} + +static uint32_t PPC_prep_io_readl (void *opaque, target_phys_addr_t addr) +{ + sysctrl_t *sysctrl = opaque; + uint32_t ret; + + addr = prep_IO_address(sysctrl, addr); + ret = cpu_inl(NULL, addr); +#ifdef TARGET_WORDS_BIGENDIAN + ret = bswap32(ret); +#endif + PPC_IO_DPRINTF("0x%08lx <= 0x%08x\n", (long)addr, ret); + + return ret; +} + +CPUWriteMemoryFunc *PPC_prep_io_write[] = { + &PPC_prep_io_writeb, + &PPC_prep_io_writew, + &PPC_prep_io_writel, +}; + +CPUReadMemoryFunc *PPC_prep_io_read[] = { + &PPC_prep_io_readb, + &PPC_prep_io_readw, + &PPC_prep_io_readl, +}; + +#define NVRAM_SIZE 0x2000 + +/* PowerPC PREP hardware initialisation */ +static void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + CPUState *env; + char buf[1024]; + SetIRQFunc *set_irq; + m48t59_t *nvram; + int PPC_io_memory; + int linux_boot, i, nb_nics1, bios_size; + unsigned long bios_offset; + uint32_t kernel_base, kernel_size, initrd_base, initrd_size; + ppc_def_t *def; + PCIBus *pci_bus; + + sysctrl = qemu_mallocz(sizeof(sysctrl_t)); + if (sysctrl == NULL) + return; + + linux_boot = (kernel_filename != NULL); + + /* init CPUs */ + + env = cpu_init(); + register_savevm("cpu", 0, 3, cpu_save, cpu_load, env); + + /* Register CPU as a 604 */ + /* XXX: CPU model (or PVR) should be provided on command line */ + // ppc_find_by_name("604r", &def); + // ppc_find_by_name("604e", &def); + ppc_find_by_name("604", &def); + if (def == NULL) { + cpu_abort(env, "Unable to find PowerPC CPU definition\n"); + } + cpu_ppc_register(env, def); + /* Set time-base frequency to 100 Mhz */ + cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); + + /* allocate RAM */ + cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); + + /* allocate and load BIOS */ + bios_offset = ram_size + vga_ram_size; + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME); + bios_size = load_image(buf, phys_ram_base + bios_offset); + if (bios_size < 0 || bios_size > BIOS_SIZE) { + fprintf(stderr, "qemu: could not load PPC PREP bios '%s'\n", buf); + exit(1); + } + bios_size = (bios_size + 0xfff) & ~0xfff; + cpu_register_physical_memory((uint32_t)(-bios_size), + bios_size, bios_offset | IO_MEM_ROM); + + if (linux_boot) { + kernel_base = KERNEL_LOAD_ADDR; + /* now we can load the kernel */ + kernel_size = load_image(kernel_filename, phys_ram_base + kernel_base); + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + /* load initrd */ + if (initrd_filename) { + initrd_base = INITRD_LOAD_ADDR; + initrd_size = load_image(initrd_filename, + phys_ram_base + initrd_base); + if (initrd_size < 0) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + } else { + initrd_base = 0; + initrd_size = 0; + } + boot_device = 'm'; + } else { + kernel_base = 0; + kernel_size = 0; + initrd_base = 0; + initrd_size = 0; + } + + isa_mem_base = 0xc0000000; + pci_bus = pci_prep_init(); + // pci_bus = i440fx_init(); + /* Register 8 MB of ISA IO space (needed for non-contiguous map) */ + PPC_io_memory = cpu_register_io_memory(0, PPC_prep_io_read, + PPC_prep_io_write, sysctrl); + cpu_register_physical_memory(0x80000000, 0x00800000, PPC_io_memory); + + /* init basic PC hardware */ + vga_initialize(pci_bus, ds, phys_ram_base + ram_size, ram_size, + vga_ram_size, 0, 0); + rtc_init(0x70, 8); + // openpic = openpic_init(0x00000000, 0xF0000000, 1); + isa_pic = pic_init(pic_irq_request, first_cpu); + // pit = pit_init(0x40, 0); + + serial_init(&pic_set_irq_new, isa_pic, 0x3f8, 4, serial_hds[0]); + nb_nics1 = nb_nics; + if (nb_nics1 > NE2000_NB_MAX) + nb_nics1 = NE2000_NB_MAX; + for(i = 0; i < nb_nics1; i++) { + if (nd_table[0].model == NULL + || strcmp(nd_table[0].model, "ne2k_isa") == 0) { + isa_ne2000_init(ne2000_io[i], ne2000_irq[i], &nd_table[i]); + } else { + fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model); + exit (1); + } + } + + for(i = 0; i < 2; i++) { + isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], + bs_table[2 * i], bs_table[2 * i + 1]); + } + kbd_init(); + DMA_init(1); + // AUD_init(); + // SB16_init(); + + fdctrl_init(6, 2, 0, 0x3f0, fd_table); + + /* Register speaker port */ + register_ioport_read(0x61, 1, 1, speaker_ioport_read, NULL); + register_ioport_write(0x61, 1, 1, speaker_ioport_write, NULL); + /* Register fake IO ports for PREP */ + register_ioport_read(0x398, 2, 1, &PREP_io_read, sysctrl); + register_ioport_write(0x398, 2, 1, &PREP_io_write, sysctrl); + /* System control ports */ + register_ioport_read(0x0092, 0x01, 1, &PREP_io_800_readb, sysctrl); + register_ioport_write(0x0092, 0x01, 1, &PREP_io_800_writeb, sysctrl); + register_ioport_read(0x0800, 0x52, 1, &PREP_io_800_readb, sysctrl); + register_ioport_write(0x0800, 0x52, 1, &PREP_io_800_writeb, sysctrl); + /* PCI intack location */ + PPC_io_memory = cpu_register_io_memory(0, PPC_intack_read, + PPC_intack_write, NULL); + cpu_register_physical_memory(0xBFFFFFF0, 0x4, PPC_io_memory); + /* PowerPC control and status register group */ +#if 0 + PPC_io_memory = cpu_register_io_memory(0, PPC_XCSR_read, PPC_XCSR_write, NULL); + cpu_register_physical_memory(0xFEFF0000, 0x1000, PPC_io_memory); +#endif + + nvram = m48t59_init(8, 0, 0x0074, NVRAM_SIZE, 59); + if (nvram == NULL) + return; + sysctrl->nvram = nvram; + + /* Initialise NVRAM */ + PPC_NVRAM_set_params(nvram, NVRAM_SIZE, "PREP", ram_size, boot_device, + kernel_base, kernel_size, + kernel_cmdline, + initrd_base, initrd_size, + /* XXX: need an option to load a NVRAM image */ + 0, + graphic_width, graphic_height, graphic_depth); + + /* Special port to get debug messages from Open-Firmware */ + register_ioport_write(0x0F00, 4, 1, &PPC_debug_write, NULL); +} + +QEMUMachine prep_machine = { + "prep", + "PowerPC PREP platform", + ppc_prep_init, +}; diff --git a/tools/ioemu/hw/ps2.c b/tools/ioemu/hw/ps2.c new file mode 100644 index 0000000000..8438a5e853 --- /dev/null +++ b/tools/ioemu/hw/ps2.c @@ -0,0 +1,566 @@ +/* + * QEMU PS/2 keyboard/mouse emulation + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* debug PC keyboard */ +//#define DEBUG_KBD + +/* debug PC keyboard : only mouse */ +//#define DEBUG_MOUSE + +/* Keyboard Commands */ +#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ +#define KBD_CMD_ECHO 0xEE +#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */ +#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ +#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ +#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */ +#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */ +#define KBD_CMD_RESET 0xFF /* Reset */ + +/* Keyboard Replies */ +#define KBD_REPLY_POR 0xAA /* Power on reset */ +#define KBD_REPLY_ACK 0xFA /* Command ACK */ +#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ + +/* Mouse Commands */ +#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ +#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ +#define AUX_SET_RES 0xE8 /* Set resolution */ +#define AUX_GET_SCALE 0xE9 /* Get scaling factor */ +#define AUX_SET_STREAM 0xEA /* Set stream mode */ +#define AUX_POLL 0xEB /* Poll */ +#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */ +#define AUX_SET_WRAP 0xEE /* Set wrap mode */ +#define AUX_SET_REMOTE 0xF0 /* Set remote mode */ +#define AUX_GET_TYPE 0xF2 /* Get type */ +#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ +#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ +#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ +#define AUX_SET_DEFAULT 0xF6 +#define AUX_RESET 0xFF /* Reset aux device */ +#define AUX_ACK 0xFA /* Command byte ACK. */ + +#define MOUSE_STATUS_REMOTE 0x40 +#define MOUSE_STATUS_ENABLED 0x20 +#define MOUSE_STATUS_SCALE21 0x10 + +#define PS2_QUEUE_SIZE 256 + +typedef struct { + uint8_t data[PS2_QUEUE_SIZE]; + int rptr, wptr, count; +} PS2Queue; + +typedef struct { + PS2Queue queue; + int32_t write_cmd; + void (*update_irq)(void *, int); + void *update_arg; +} PS2State; + +typedef struct { + PS2State common; + int scan_enabled; + /* Qemu uses translated PC scancodes internally. To avoid multiple + conversions we do the translation (if any) in the PS/2 emulation + not the keyboard controller. */ + int translate; +} PS2KbdState; + +typedef struct { + PS2State common; + uint8_t mouse_status; + uint8_t mouse_resolution; + uint8_t mouse_sample_rate; + uint8_t mouse_wrap; + uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */ + uint8_t mouse_detect_state; + int mouse_dx; /* current values, needed for 'poll' mode */ + int mouse_dy; + int mouse_dz; + uint8_t mouse_buttons; +} PS2MouseState; + +/* Table to convert from PC scancodes to raw scancodes. */ +static const unsigned char ps2_raw_keycode[128] = { + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 +}; + +void ps2_queue(void *opaque, int b) +{ + PS2State *s = (PS2State *)opaque; + PS2Queue *q = &s->queue; + + if (q->count >= PS2_QUEUE_SIZE) + return; + q->data[q->wptr] = b; + if (++q->wptr == PS2_QUEUE_SIZE) + q->wptr = 0; + q->count++; + s->update_irq(s->update_arg, 1); +} + +static void ps2_put_keycode(void *opaque, int keycode) +{ + PS2KbdState *s = opaque; + if (!s->translate && keycode < 0xe0) + { + if (keycode & 0x80) + ps2_queue(&s->common, 0xf0); + keycode = ps2_raw_keycode[keycode & 0x7f]; + } + ps2_queue(&s->common, keycode); +} + +uint32_t ps2_read_data(void *opaque) +{ + PS2State *s = (PS2State *)opaque; + PS2Queue *q; + int val, index; + + q = &s->queue; + if (q->count == 0) { + /* NOTE: if no data left, we return the last keyboard one + (needed for EMM386) */ + /* XXX: need a timer to do things correctly */ + index = q->rptr - 1; + if (index < 0) + index = PS2_QUEUE_SIZE - 1; + val = q->data[index]; + } else { + val = q->data[q->rptr]; + if (++q->rptr == PS2_QUEUE_SIZE) + q->rptr = 0; + q->count--; + /* reading deasserts IRQ */ + s->update_irq(s->update_arg, 0); + /* reassert IRQs if data left */ + s->update_irq(s->update_arg, q->count != 0); + } + return val; +} + +static void ps2_reset_keyboard(PS2KbdState *s) +{ + s->scan_enabled = 1; +} + +void ps2_write_keyboard(void *opaque, int val) +{ + PS2KbdState *s = (PS2KbdState *)opaque; + + switch(s->common.write_cmd) { + default: + case -1: + switch(val) { + case 0x00: + ps2_queue(&s->common, KBD_REPLY_ACK); + break; + case 0x05: + ps2_queue(&s->common, KBD_REPLY_RESEND); + break; + case KBD_CMD_GET_ID: + ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_queue(&s->common, 0xab); + ps2_queue(&s->common, 0x83); + break; + case KBD_CMD_ECHO: + ps2_queue(&s->common, KBD_CMD_ECHO); + break; + case KBD_CMD_ENABLE: + s->scan_enabled = 1; + ps2_queue(&s->common, KBD_REPLY_ACK); + break; + case KBD_CMD_SET_LEDS: + case KBD_CMD_SET_RATE: + s->common.write_cmd = val; + ps2_queue(&s->common, KBD_REPLY_ACK); + break; + case KBD_CMD_RESET_DISABLE: + ps2_reset_keyboard(s); + s->scan_enabled = 0; + ps2_queue(&s->common, KBD_REPLY_ACK); + break; + case KBD_CMD_RESET_ENABLE: + ps2_reset_keyboard(s); + s->scan_enabled = 1; + ps2_queue(&s->common, KBD_REPLY_ACK); + break; + case KBD_CMD_RESET: + ps2_reset_keyboard(s); + ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_queue(&s->common, KBD_REPLY_POR); + break; + default: + ps2_queue(&s->common, KBD_REPLY_ACK); + break; + } + break; + case KBD_CMD_SET_LEDS: + ps2_queue(&s->common, KBD_REPLY_ACK); + s->common.write_cmd = -1; + break; + case KBD_CMD_SET_RATE: + ps2_queue(&s->common, KBD_REPLY_ACK); + s->common.write_cmd = -1; + break; + } +} + +/* Set the scancode translation mode. + 0 = raw scancodes. + 1 = translated scancodes (used by qemu internally). */ + +void ps2_keyboard_set_translation(void *opaque, int mode) +{ + PS2KbdState *s = (PS2KbdState *)opaque; + s->translate = mode; +} + +static void ps2_mouse_send_packet(PS2MouseState *s) +{ + unsigned int b; + int dx1, dy1, dz1; + + dx1 = s->mouse_dx; + dy1 = s->mouse_dy; + dz1 = s->mouse_dz; + /* XXX: increase range to 8 bits ? */ + if (dx1 > 127) + dx1 = 127; + else if (dx1 < -127) + dx1 = -127; + if (dy1 > 127) + dy1 = 127; + else if (dy1 < -127) + dy1 = -127; + b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07); + ps2_queue(&s->common, b); + ps2_queue(&s->common, dx1 & 0xff); + ps2_queue(&s->common, dy1 & 0xff); + /* extra byte for IMPS/2 or IMEX */ + switch(s->mouse_type) { + default: + break; + case 3: + if (dz1 > 127) + dz1 = 127; + else if (dz1 < -127) + dz1 = -127; + ps2_queue(&s->common, dz1 & 0xff); + break; + case 4: + if (dz1 > 7) + dz1 = 7; + else if (dz1 < -7) + dz1 = -7; + b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1); + ps2_queue(&s->common, b); + break; + } + + /* update deltas */ + s->mouse_dx -= dx1; + s->mouse_dy -= dy1; + s->mouse_dz -= dz1; +} + +static void ps2_mouse_event(void *opaque, + int dx, int dy, int dz, int buttons_state) +{ + PS2MouseState *s = opaque; + + /* check if deltas are recorded when disabled */ + if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) + return; + + s->mouse_dx += dx; + s->mouse_dy -= dy; + s->mouse_dz += dz; + /* XXX: SDL sometimes generates nul events: we delete them */ + if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 && + s->mouse_buttons == buttons_state) + return; + s->mouse_buttons = buttons_state; + + if (!(s->mouse_status & MOUSE_STATUS_REMOTE) && + (s->common.queue.count < (PS2_QUEUE_SIZE - 16))) { + for(;;) { + /* if not remote, send event. Multiple events are sent if + too big deltas */ + ps2_mouse_send_packet(s); + if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0) + break; + } + } +} + +void ps2_write_mouse(void *opaque, int val) +{ + PS2MouseState *s = (PS2MouseState *)opaque; +#ifdef DEBUG_MOUSE + printf("kbd: write mouse 0x%02x\n", val); +#endif + switch(s->common.write_cmd) { + default: + case -1: + /* mouse command */ + if (s->mouse_wrap) { + if (val == AUX_RESET_WRAP) { + s->mouse_wrap = 0; + ps2_queue(&s->common, AUX_ACK); + return; + } else if (val != AUX_RESET) { + ps2_queue(&s->common, val); + return; + } + } + switch(val) { + case AUX_SET_SCALE11: + s->mouse_status &= ~MOUSE_STATUS_SCALE21; + ps2_queue(&s->common, AUX_ACK); + break; + case AUX_SET_SCALE21: + s->mouse_status |= MOUSE_STATUS_SCALE21; + ps2_queue(&s->common, AUX_ACK); + break; + case AUX_SET_STREAM: + s->mouse_status &= ~MOUSE_STATUS_REMOTE; + ps2_queue(&s->common, AUX_ACK); + break; + case AUX_SET_WRAP: + s->mouse_wrap = 1; + ps2_queue(&s->common, AUX_ACK); + break; + case AUX_SET_REMOTE: + s->mouse_status |= MOUSE_STATUS_REMOTE; + ps2_queue(&s->common, AUX_ACK); + break; + case AUX_GET_TYPE: + ps2_queue(&s->common, AUX_ACK); + ps2_queue(&s->common, s->mouse_type); + break; + case AUX_SET_RES: + case AUX_SET_SAMPLE: + s->common.write_cmd = val; + ps2_queue(&s->common, AUX_ACK); + break; + case AUX_GET_SCALE: + ps2_queue(&s->common, AUX_ACK); + ps2_queue(&s->common, s->mouse_status); + ps2_queue(&s->common, s->mouse_resolution); + ps2_queue(&s->common, s->mouse_sample_rate); + break; + case AUX_POLL: + ps2_queue(&s->common, AUX_ACK); + ps2_mouse_send_packet(s); + break; + case AUX_ENABLE_DEV: + s->mouse_status |= MOUSE_STATUS_ENABLED; + ps2_queue(&s->common, AUX_ACK); + break; + case AUX_DISABLE_DEV: + s->mouse_status &= ~MOUSE_STATUS_ENABLED; + ps2_queue(&s->common, AUX_ACK); + break; + case AUX_SET_DEFAULT: + s->mouse_sample_rate = 100; + s->mouse_resolution = 2; + s->mouse_status = 0; + ps2_queue(&s->common, AUX_ACK); + break; + case AUX_RESET: + s->mouse_sample_rate = 100; + s->mouse_resolution = 2; + s->mouse_status = 0; + s->mouse_type = 0; + ps2_queue(&s->common, AUX_ACK); + ps2_queue(&s->common, 0xaa); + ps2_queue(&s->common, s->mouse_type); + break; + default: + break; + } + break; + case AUX_SET_SAMPLE: + s->mouse_sample_rate = val; + /* detect IMPS/2 or IMEX */ + switch(s->mouse_detect_state) { + default: + case 0: + if (val == 200) + s->mouse_detect_state = 1; + break; + case 1: + if (val == 100) + s->mouse_detect_state = 2; + else if (val == 200) + s->mouse_detect_state = 3; + else + s->mouse_detect_state = 0; + break; + case 2: + if (val == 80) + s->mouse_type = 3; /* IMPS/2 */ + s->mouse_detect_state = 0; + break; + case 3: + if (val == 80) + s->mouse_type = 4; /* IMEX */ + s->mouse_detect_state = 0; + break; + } + ps2_queue(&s->common, AUX_ACK); + s->common.write_cmd = -1; + break; + case AUX_SET_RES: + s->mouse_resolution = val; + ps2_queue(&s->common, AUX_ACK); + s->common.write_cmd = -1; + break; + } +} + +static void ps2_reset(void *opaque) +{ + PS2State *s = (PS2State *)opaque; + PS2Queue *q; + s->write_cmd = -1; + q = &s->queue; + q->rptr = 0; + q->wptr = 0; + q->count = 0; +} + +static void ps2_common_save (QEMUFile *f, PS2State *s) +{ + qemu_put_be32s (f, &s->write_cmd); + qemu_put_be32s (f, &s->queue.rptr); + qemu_put_be32s (f, &s->queue.wptr); + qemu_put_be32s (f, &s->queue.count); + qemu_put_buffer (f, s->queue.data, sizeof (s->queue.data)); +} + +static void ps2_common_load (QEMUFile *f, PS2State *s) +{ + qemu_get_be32s (f, &s->write_cmd); + qemu_get_be32s (f, &s->queue.rptr); + qemu_get_be32s (f, &s->queue.wptr); + qemu_get_be32s (f, &s->queue.count); + qemu_get_buffer (f, s->queue.data, sizeof (s->queue.data)); +} + +static void ps2_kbd_save(QEMUFile* f, void* opaque) +{ + PS2KbdState *s = (PS2KbdState*)opaque; + + ps2_common_save (f, &s->common); + qemu_put_be32s(f, &s->scan_enabled); + qemu_put_be32s(f, &s->translate); +} + +static void ps2_mouse_save(QEMUFile* f, void* opaque) +{ + PS2MouseState *s = (PS2MouseState*)opaque; + + ps2_common_save (f, &s->common); + qemu_put_8s(f, &s->mouse_status); + qemu_put_8s(f, &s->mouse_resolution); + qemu_put_8s(f, &s->mouse_sample_rate); + qemu_put_8s(f, &s->mouse_wrap); + qemu_put_8s(f, &s->mouse_type); + qemu_put_8s(f, &s->mouse_detect_state); + qemu_put_be32s(f, &s->mouse_dx); + qemu_put_be32s(f, &s->mouse_dy); + qemu_put_be32s(f, &s->mouse_dz); + qemu_put_8s(f, &s->mouse_buttons); +} + +static int ps2_kbd_load(QEMUFile* f, void* opaque, int version_id) +{ + PS2KbdState *s = (PS2KbdState*)opaque; + + if (version_id != 2) + return -EINVAL; + + ps2_common_load (f, &s->common); + qemu_get_be32s(f, &s->scan_enabled); + qemu_get_be32s(f, &s->translate); + return 0; +} + +static int ps2_mouse_load(QEMUFile* f, void* opaque, int version_id) +{ + PS2MouseState *s = (PS2MouseState*)opaque; + + if (version_id != 2) + return -EINVAL; + + ps2_common_load (f, &s->common); + qemu_get_8s(f, &s->mouse_status); + qemu_get_8s(f, &s->mouse_resolution); + qemu_get_8s(f, &s->mouse_sample_rate); + qemu_get_8s(f, &s->mouse_wrap); + qemu_get_8s(f, &s->mouse_type); + qemu_get_8s(f, &s->mouse_detect_state); + qemu_get_be32s(f, &s->mouse_dx); + qemu_get_be32s(f, &s->mouse_dy); + qemu_get_be32s(f, &s->mouse_dz); + qemu_get_8s(f, &s->mouse_buttons); + return 0; +} + +void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg) +{ + PS2KbdState *s = (PS2KbdState *)qemu_mallocz(sizeof(PS2KbdState)); + + s->common.update_irq = update_irq; + s->common.update_arg = update_arg; + ps2_reset(&s->common); + register_savevm("ps2kbd", 0, 2, ps2_kbd_save, ps2_kbd_load, s); + qemu_add_kbd_event_handler(ps2_put_keycode, s); + qemu_register_reset(ps2_reset, &s->common); + return s; +} + +void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg) +{ + PS2MouseState *s = (PS2MouseState *)qemu_mallocz(sizeof(PS2MouseState)); + + s->common.update_irq = update_irq; + s->common.update_arg = update_arg; + ps2_reset(&s->common); + register_savevm("ps2mouse", 0, 2, ps2_mouse_save, ps2_mouse_load, s); + qemu_add_mouse_event_handler(ps2_mouse_event, s, 0); + qemu_register_reset(ps2_reset, &s->common); + return s; +} diff --git a/tools/ioemu/hw/rtl8139.c b/tools/ioemu/hw/rtl8139.c new file mode 100644 index 0000000000..49c4c916b7 --- /dev/null +++ b/tools/ioemu/hw/rtl8139.c @@ -0,0 +1,2875 @@ +/** + * QEMU RTL8139 emulation + * + * Copyright (c) 2006 Igor Kovalenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + + * Modifications: + * 2006-Jan-28 Mark Malakanov : TSAD and CSCR implementation (for Windows driver) + * + */ + +#include "vl.h" + + +/* debug RTL8139 card */ +//#define DEBUG_RTL8139 1 + +/* debug RTL8139 card C+ mode only */ +//#define DEBUG_RTL8139CP 1 + +/* RTL8139 provides frame CRC with received packet, this feature seems to be + ignored by most drivers, disabled by default */ +//#define RTL8139_CALCULATE_RXCRC 1 + + +#if defined(RTL8139_CALCULATE_RXCRC) +/* For crc32 */ +#include +#endif + +#define SET_MASKED(input, mask, curr) \ + ( ( (input) & ~(mask) ) | ( (curr) & (mask) ) ) + +/* arg % size for size which is a power of 2 */ +#define MOD2(input, size) \ + ( ( input ) & ( size - 1 ) ) + +/* Symbolic offsets to registers. */ +enum RTL8139_registers { + MAC0 = 0, /* Ethernet hardware address. */ + MAR0 = 8, /* Multicast filter. */ + TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */ + TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */ + RxBuf = 0x30, + ChipCmd = 0x37, + RxBufPtr = 0x38, + RxBufAddr = 0x3A, + IntrMask = 0x3C, + IntrStatus = 0x3E, + TxConfig = 0x40, + RxConfig = 0x44, + Timer = 0x48, /* A general-purpose counter. */ + RxMissed = 0x4C, /* 24 bits valid, write clears. */ + Cfg9346 = 0x50, + Config0 = 0x51, + Config1 = 0x52, + FlashReg = 0x54, + MediaStatus = 0x58, + Config3 = 0x59, + Config4 = 0x5A, /* absent on RTL-8139A */ + HltClk = 0x5B, + MultiIntr = 0x5C, + PCIRevisionID = 0x5E, + TxSummary = 0x60, /* TSAD register. Transmit Status of All Descriptors*/ + BasicModeCtrl = 0x62, + BasicModeStatus = 0x64, + NWayAdvert = 0x66, + NWayLPAR = 0x68, + NWayExpansion = 0x6A, + /* Undocumented registers, but required for proper operation. */ + FIFOTMS = 0x70, /* FIFO Control and test. */ + CSCR = 0x74, /* Chip Status and Configuration Register. */ + PARA78 = 0x78, + PARA7c = 0x7c, /* Magic transceiver parameter register. */ + Config5 = 0xD8, /* absent on RTL-8139A */ + /* C+ mode */ + TxPoll = 0xD9, /* Tell chip to check Tx descriptors for work */ + RxMaxSize = 0xDA, /* Max size of an Rx packet (8169 only) */ + CpCmd = 0xE0, /* C+ Command register (C+ mode only) */ + IntrMitigate = 0xE2, /* rx/tx interrupt mitigation control */ + RxRingAddrLO = 0xE4, /* 64-bit start addr of Rx ring */ + RxRingAddrHI = 0xE8, /* 64-bit start addr of Rx ring */ + TxThresh = 0xEC, /* Early Tx threshold */ +}; + +enum ClearBitMasks { + MultiIntrClear = 0xF000, + ChipCmdClear = 0xE2, + Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1), +}; + +enum ChipCmdBits { + CmdReset = 0x10, + CmdRxEnb = 0x08, + CmdTxEnb = 0x04, + RxBufEmpty = 0x01, +}; + +/* C+ mode */ +enum CplusCmdBits { + CPlusRxEnb = 0x0002, + CPlusTxEnb = 0x0001, +}; + +/* Interrupt register bits, using my own meaningful names. */ +enum IntrStatusBits { + PCIErr = 0x8000, + PCSTimeout = 0x4000, + RxFIFOOver = 0x40, + RxUnderrun = 0x20, + RxOverflow = 0x10, + TxErr = 0x08, + TxOK = 0x04, + RxErr = 0x02, + RxOK = 0x01, + + RxAckBits = RxFIFOOver | RxOverflow | RxOK, +}; + +enum TxStatusBits { + TxHostOwns = 0x2000, + TxUnderrun = 0x4000, + TxStatOK = 0x8000, + TxOutOfWindow = 0x20000000, + TxAborted = 0x40000000, + TxCarrierLost = 0x80000000, +}; +enum RxStatusBits { + RxMulticast = 0x8000, + RxPhysical = 0x4000, + RxBroadcast = 0x2000, + RxBadSymbol = 0x0020, + RxRunt = 0x0010, + RxTooLong = 0x0008, + RxCRCErr = 0x0004, + RxBadAlign = 0x0002, + RxStatusOK = 0x0001, +}; + +/* Bits in RxConfig. */ +enum rx_mode_bits { + AcceptErr = 0x20, + AcceptRunt = 0x10, + AcceptBroadcast = 0x08, + AcceptMulticast = 0x04, + AcceptMyPhys = 0x02, + AcceptAllPhys = 0x01, +}; + +/* Bits in TxConfig. */ +enum tx_config_bits { + + /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */ + TxIFGShift = 24, + TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */ + TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */ + TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */ + TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */ + + TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ + TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */ + TxClearAbt = (1 << 0), /* Clear abort (WO) */ + TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */ + TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */ + + TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */ +}; + + +/* Transmit Status of All Descriptors (TSAD) Register */ +enum TSAD_bits { + TSAD_TOK3 = 1<<15, // TOK bit of Descriptor 3 + TSAD_TOK2 = 1<<14, // TOK bit of Descriptor 2 + TSAD_TOK1 = 1<<13, // TOK bit of Descriptor 1 + TSAD_TOK0 = 1<<12, // TOK bit of Descriptor 0 + TSAD_TUN3 = 1<<11, // TUN bit of Descriptor 3 + TSAD_TUN2 = 1<<10, // TUN bit of Descriptor 2 + TSAD_TUN1 = 1<<9, // TUN bit of Descriptor 1 + TSAD_TUN0 = 1<<8, // TUN bit of Descriptor 0 + TSAD_TABT3 = 1<<07, // TABT bit of Descriptor 3 + TSAD_TABT2 = 1<<06, // TABT bit of Descriptor 2 + TSAD_TABT1 = 1<<05, // TABT bit of Descriptor 1 + TSAD_TABT0 = 1<<04, // TABT bit of Descriptor 0 + TSAD_OWN3 = 1<<03, // OWN bit of Descriptor 3 + TSAD_OWN2 = 1<<02, // OWN bit of Descriptor 2 + TSAD_OWN1 = 1<<01, // OWN bit of Descriptor 1 + TSAD_OWN0 = 1<<00, // OWN bit of Descriptor 0 +}; + + +/* Bits in Config1 */ +enum Config1Bits { + Cfg1_PM_Enable = 0x01, + Cfg1_VPD_Enable = 0x02, + Cfg1_PIO = 0x04, + Cfg1_MMIO = 0x08, + LWAKE = 0x10, /* not on 8139, 8139A */ + Cfg1_Driver_Load = 0x20, + Cfg1_LED0 = 0x40, + Cfg1_LED1 = 0x80, + SLEEP = (1 << 1), /* only on 8139, 8139A */ + PWRDN = (1 << 0), /* only on 8139, 8139A */ +}; + +/* Bits in Config3 */ +enum Config3Bits { + Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */ + Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */ + Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */ + Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */ + Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */ + Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */ + Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */ + Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */ +}; + +/* Bits in Config4 */ +enum Config4Bits { + LWPTN = (1 << 2), /* not on 8139, 8139A */ +}; + +/* Bits in Config5 */ +enum Config5Bits { + Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */ + Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */ + Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */ + Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */ + Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */ + Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */ + Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */ +}; + +enum RxConfigBits { + /* rx fifo threshold */ + RxCfgFIFOShift = 13, + RxCfgFIFONone = (7 << RxCfgFIFOShift), + + /* Max DMA burst */ + RxCfgDMAShift = 8, + RxCfgDMAUnlimited = (7 << RxCfgDMAShift), + + /* rx ring buffer length */ + RxCfgRcv8K = 0, + RxCfgRcv16K = (1 << 11), + RxCfgRcv32K = (1 << 12), + RxCfgRcv64K = (1 << 11) | (1 << 12), + + /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */ + RxNoWrap = (1 << 7), +}; + +/* Twister tuning parameters from RealTek. + Completely undocumented, but required to tune bad links on some boards. */ +/* +enum CSCRBits { + CSCR_LinkOKBit = 0x0400, + CSCR_LinkChangeBit = 0x0800, + CSCR_LinkStatusBits = 0x0f000, + CSCR_LinkDownOffCmd = 0x003c0, + CSCR_LinkDownCmd = 0x0f3c0, +*/ +enum CSCRBits { + CSCR_Testfun = 1<<15, /* 1 = Auto-neg speeds up internal timer, WO, def 0 */ + CSCR_LD = 1<<9, /* Active low TPI link disable signal. When low, TPI still transmits link pulses and TPI stays in good link state. def 1*/ + CSCR_HEART_BIT = 1<<8, /* 1 = HEART BEAT enable, 0 = HEART BEAT disable. HEART BEAT function is only valid in 10Mbps mode. def 1*/ + CSCR_JBEN = 1<<7, /* 1 = enable jabber function. 0 = disable jabber function, def 1*/ + CSCR_F_LINK_100 = 1<<6, /* Used to login force good link in 100Mbps for diagnostic purposes. 1 = DISABLE, 0 = ENABLE. def 1*/ + CSCR_F_Connect = 1<<5, /* Assertion of this bit forces the disconnect function to be bypassed. def 0*/ + CSCR_Con_status = 1<<3, /* This bit indicates the status of the connection. 1 = valid connected link detected; 0 = disconnected link detected. RO def 0*/ + CSCR_Con_status_En = 1<<2, /* Assertion of this bit configures LED1 pin to indicate connection status. def 0*/ + CSCR_PASS_SCR = 1<<0, /* Bypass Scramble, def 0*/ +}; + +enum Cfg9346Bits { + Cfg9346_Lock = 0x00, + Cfg9346_Unlock = 0xC0, +}; + +typedef enum { + CH_8139 = 0, + CH_8139_K, + CH_8139A, + CH_8139A_G, + CH_8139B, + CH_8130, + CH_8139C, + CH_8100, + CH_8100B_8139D, + CH_8101, +} chip_t; + +enum chip_flags { + HasHltClk = (1 << 0), + HasLWake = (1 << 1), +}; + +#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \ + (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22) +#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1) + +/* Size is 64 * 16bit words */ +#define EEPROM_9346_ADDR_BITS 6 +#define EEPROM_9346_SIZE (1 << EEPROM_9346_ADDR_BITS) +#define EEPROM_9346_ADDR_MASK (EEPROM_9346_SIZE - 1) + +enum Chip9346Operation +{ + Chip9346_op_mask = 0xc0, /* 10 zzzzzz */ + Chip9346_op_read = 0x80, /* 10 AAAAAA */ + Chip9346_op_write = 0x40, /* 01 AAAAAA D(15)..D(0) */ + Chip9346_op_ext_mask = 0xf0, /* 11 zzzzzz */ + Chip9346_op_write_enable = 0x30, /* 00 11zzzz */ + Chip9346_op_write_all = 0x10, /* 00 01zzzz */ + Chip9346_op_write_disable = 0x00, /* 00 00zzzz */ +}; + +enum Chip9346Mode +{ + Chip9346_none = 0, + Chip9346_enter_command_mode, + Chip9346_read_command, + Chip9346_data_read, /* from output register */ + Chip9346_data_write, /* to input register, then to contents at specified address */ + Chip9346_data_write_all, /* to input register, then filling contents */ +}; + +typedef struct EEprom9346 +{ + uint16_t contents[EEPROM_9346_SIZE]; + int mode; + uint32_t tick; + uint8_t address; + uint16_t input; + uint16_t output; + + uint8_t eecs; + uint8_t eesk; + uint8_t eedi; + uint8_t eedo; +} EEprom9346; + +typedef struct RTL8139State { + uint8_t phys[8]; /* mac address */ + uint8_t mult[8]; /* multicast mask array */ + + uint32_t TxStatus[4]; /* TxStatus0 */ + uint32_t TxAddr[4]; /* TxAddr0 */ + uint32_t RxBuf; /* Receive buffer */ + uint32_t RxBufferSize;/* internal variable, receive ring buffer size in C mode */ + uint32_t RxBufPtr; + uint32_t RxBufAddr; + + uint16_t IntrStatus; + uint16_t IntrMask; + + uint32_t TxConfig; + uint32_t RxConfig; + uint32_t RxMissed; + + uint16_t CSCR; + + uint8_t Cfg9346; + uint8_t Config0; + uint8_t Config1; + uint8_t Config3; + uint8_t Config4; + uint8_t Config5; + + uint8_t clock_enabled; + uint8_t bChipCmdState; + + uint16_t MultiIntr; + + uint16_t BasicModeCtrl; + uint16_t BasicModeStatus; + uint16_t NWayAdvert; + uint16_t NWayLPAR; + uint16_t NWayExpansion; + + uint16_t CpCmd; + uint8_t TxThresh; + + int irq; + PCIDevice *pci_dev; + VLANClientState *vc; + uint8_t macaddr[6]; + int rtl8139_mmio_io_addr; + + /* C ring mode */ + uint32_t currTxDesc; + + /* C+ mode */ + uint32_t currCPlusRxDesc; + uint32_t currCPlusTxDesc; + + uint32_t RxRingAddrLO; + uint32_t RxRingAddrHI; + + EEprom9346 eeprom; + +} RTL8139State; + +void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command) +{ +#if defined(DEBUG_RTL8139) + printf("RTL8139: eeprom command 0x%02x\n", command); +#endif + + switch (command & Chip9346_op_mask) + { + case Chip9346_op_read: + { + eeprom->address = command & EEPROM_9346_ADDR_MASK; + eeprom->output = eeprom->contents[eeprom->address]; + eeprom->eedo = 0; + eeprom->tick = 0; + eeprom->mode = Chip9346_data_read; +#if defined(DEBUG_RTL8139) + printf("RTL8139: eeprom read from address 0x%02x data=0x%04x\n", + eeprom->address, eeprom->output); +#endif + } + break; + + case Chip9346_op_write: + { + eeprom->address = command & EEPROM_9346_ADDR_MASK; + eeprom->input = 0; + eeprom->tick = 0; + eeprom->mode = Chip9346_none; /* Chip9346_data_write */ +#if defined(DEBUG_RTL8139) + printf("RTL8139: eeprom begin write to address 0x%02x\n", + eeprom->address); +#endif + } + break; + default: + eeprom->mode = Chip9346_none; + switch (command & Chip9346_op_ext_mask) + { + case Chip9346_op_write_enable: +#if defined(DEBUG_RTL8139) + printf("RTL8139: eeprom write enabled\n"); +#endif + break; + case Chip9346_op_write_all: +#if defined(DEBUG_RTL8139) + printf("RTL8139: eeprom begin write all\n"); +#endif + break; + case Chip9346_op_write_disable: +#if defined(DEBUG_RTL8139) + printf("RTL8139: eeprom write disabled\n"); +#endif + break; + } + break; + } +} + +void prom9346_shift_clock(EEprom9346 *eeprom) +{ + int bit = eeprom->eedi?1:0; + + ++ eeprom->tick; + +#if defined(DEBUG_RTL8139) + printf("eeprom: tick %d eedi=%d eedo=%d\n", eeprom->tick, eeprom->eedi, eeprom->eedo); +#endif + + switch (eeprom->mode) + { + case Chip9346_enter_command_mode: + if (bit) + { + eeprom->mode = Chip9346_read_command; + eeprom->tick = 0; + eeprom->input = 0; +#if defined(DEBUG_RTL8139) + printf("eeprom: +++ synchronized, begin command read\n"); +#endif + } + break; + + case Chip9346_read_command: + eeprom->input = (eeprom->input << 1) | (bit & 1); + if (eeprom->tick == 8) + { + prom9346_decode_command(eeprom, eeprom->input & 0xff); + } + break; + + case Chip9346_data_read: + eeprom->eedo = (eeprom->output & 0x8000)?1:0; + eeprom->output <<= 1; + if (eeprom->tick == 16) + { + ++eeprom->address; + eeprom->address &= EEPROM_9346_ADDR_MASK; + eeprom->output = eeprom->contents[eeprom->address]; + eeprom->tick = 0; + +#if defined(DEBUG_RTL8139) + printf("eeprom: +++ read next address 0x%02x data=0x%04x\n", + eeprom->address, eeprom->output); +#endif + } + break; + + case Chip9346_data_write: + eeprom->input = (eeprom->input << 1) | (bit & 1); + if (eeprom->tick == 16) + { +#if defined(DEBUG_RTL8139) + printf("RTL8139: eeprom write to address 0x%02x data=0x%04x\n", + eeprom->address, eeprom->input); +#endif + eeprom->contents[eeprom->address] = eeprom->input; + eeprom->mode = Chip9346_none; /* waiting for next command after CS cycle */ + eeprom->tick = 0; + eeprom->input = 0; + } + break; + + case Chip9346_data_write_all: + eeprom->input = (eeprom->input << 1) | (bit & 1); + if (eeprom->tick == 16) + { + int i; + for (i = 0; i < EEPROM_9346_SIZE; i++) + { + eeprom->contents[i] = eeprom->input; + } +#if defined(DEBUG_RTL8139) + printf("RTL8139: eeprom filled with data=0x%04x\n", + eeprom->input); +#endif + eeprom->mode = Chip9346_enter_command_mode; + eeprom->tick = 0; + eeprom->input = 0; + } + break; + + default: + break; + } +} + +int prom9346_get_wire(RTL8139State *s) +{ + EEprom9346 *eeprom = &s->eeprom; + if (!eeprom->eecs) + return 0; + + return eeprom->eedo; +} + +void prom9346_set_wire(RTL8139State *s, int eecs, int eesk, int eedi) +{ + EEprom9346 *eeprom = &s->eeprom; + uint8_t old_eecs = eeprom->eecs; + uint8_t old_eesk = eeprom->eesk; + + eeprom->eecs = eecs; + eeprom->eesk = eesk; + eeprom->eedi = eedi; + +#if defined(DEBUG_RTL8139) + printf("eeprom: +++ wires CS=%d SK=%d DI=%d DO=%d\n", eeprom->eecs, eeprom->eesk, eeprom->eedi, eeprom->eedo); +#endif + + if (!old_eecs && eecs) + { + /* Synchronize start */ + eeprom->tick = 0; + eeprom->input = 0; + eeprom->output = 0; + eeprom->mode = Chip9346_enter_command_mode; + +#if defined(DEBUG_RTL8139) + printf("=== eeprom: begin access, enter command mode\n"); +#endif + + } + + if (!eecs) + { +#if defined(DEBUG_RTL8139) + printf("=== eeprom: end access\n"); +#endif + return; + } + + if (!old_eesk && eesk) + { + /* SK front rules */ + prom9346_shift_clock(eeprom); + } +} + +static void rtl8139_update_irq(RTL8139State *s) +{ + int isr; + isr = (s->IntrStatus & s->IntrMask) & 0xffff; +#if defined(DEBUG_RTL8139) + printf("RTL8139: Set IRQ line %d to %d (%04x %04x)\n", + s->irq, isr ? 1 : 0, s->IntrStatus, s->IntrMask); +#endif + if (s->irq == 16) { + /* PCI irq */ + pci_set_irq(s->pci_dev, 0, (isr != 0)); + } else { + /* ISA irq */ + pic_set_irq(s->irq, (isr != 0)); + } +} + +#define POLYNOMIAL 0x04c11db6 + +/* From FreeBSD */ +/* XXX: optimize */ +static int compute_mcast_idx(const uint8_t *ep) +{ + uint32_t crc; + int carry, i, j; + uint8_t b; + + crc = 0xffffffff; + for (i = 0; i < 6; i++) { + b = *ep++; + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); + crc <<= 1; + b >>= 1; + if (carry) + crc = ((crc ^ POLYNOMIAL) | carry); + } + } + return (crc >> 26); +} + +static int rtl8139_RxWrap(RTL8139State *s) +{ + /* wrapping enabled; assume 1.5k more buffer space if size < 65536 */ + return (s->RxConfig & (1 << 7)); +} + +static int rtl8139_receiver_enabled(RTL8139State *s) +{ + return s->bChipCmdState & CmdRxEnb; +} + +static int rtl8139_transmitter_enabled(RTL8139State *s) +{ + return s->bChipCmdState & CmdTxEnb; +} + +static int rtl8139_cp_receiver_enabled(RTL8139State *s) +{ + return s->CpCmd & CPlusRxEnb; +} + +static int rtl8139_cp_transmitter_enabled(RTL8139State *s) +{ + return s->CpCmd & CPlusTxEnb; +} + +static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size) +{ + if (s->RxBufAddr + size > s->RxBufferSize) + { + int wrapped = MOD2(s->RxBufAddr + size, s->RxBufferSize); + + /* write packet data */ + if (wrapped && s->RxBufferSize < 65536 && !rtl8139_RxWrap(s)) + { + #if defined(DEBUG_RTL8139) + printf(">>> RTL8139: rx packet wrapped in buffer at %d\n", size-wrapped); + #endif + + if (size > wrapped) + { + cpu_physical_memory_write( s->RxBuf + s->RxBufAddr, + buf, size-wrapped ); + } + + /* reset buffer pointer */ + s->RxBufAddr = 0; + + cpu_physical_memory_write( s->RxBuf + s->RxBufAddr, + buf + (size-wrapped), wrapped ); + + s->RxBufAddr = wrapped; + + return; + } + } + + /* non-wrapping path or overwrapping enabled */ + cpu_physical_memory_write( s->RxBuf + s->RxBufAddr, buf, size ); + + s->RxBufAddr += size; +} + +#define MIN_BUF_SIZE 60 +static inline target_phys_addr_t rtl8139_addr64(uint32_t low, uint32_t high) +{ +#if TARGET_PHYS_ADDR_BITS > 32 + return low | ((target_phys_addr_t)high << 32); +#else + return low; +#endif +} + +static int rtl8139_can_receive(void *opaque) +{ + RTL8139State *s = opaque; + int avail; + + /* Recieve (drop) packets if card is disabled. */ + if (!s->clock_enabled) + return 1; + if (!rtl8139_receiver_enabled(s)) + return 1; + + if (rtl8139_cp_receiver_enabled(s)) { + /* ??? Flow control not implemented in c+ mode. + This is a hack to work around slirp deficiencies anyway. */ + return 1; + } else { + avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, + s->RxBufferSize); + return (avail == 0 || avail >= 1514); + } +} + +static void rtl8139_receive(void *opaque, const uint8_t *buf, int size) +{ + RTL8139State *s = opaque; + + uint32_t packet_header = 0; + + uint8_t buf1[60]; + static const uint8_t broadcast_macaddr[6] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +#if defined(DEBUG_RTL8139) + printf(">>> RTL8139: received len=%d\n", size); +#endif + + /* test if board clock is stopped */ + if (!s->clock_enabled) + { +#if defined(DEBUG_RTL8139) + printf("RTL8139: stopped ==========================\n"); +#endif + return; + } + + /* first check if receiver is enabled */ + + if (!rtl8139_receiver_enabled(s)) + { +#if defined(DEBUG_RTL8139) + printf("RTL8139: receiver disabled ================\n"); +#endif + return; + } + + /* XXX: check this */ + if (s->RxConfig & AcceptAllPhys) { + /* promiscuous: receive all */ +#if defined(DEBUG_RTL8139) + printf(">>> RTL8139: packet received in promiscuous mode\n"); +#endif + + } else { + if (!memcmp(buf, broadcast_macaddr, 6)) { + /* broadcast address */ + if (!(s->RxConfig & AcceptBroadcast)) + { +#if defined(DEBUG_RTL8139) + printf(">>> RTL8139: broadcast packet rejected\n"); +#endif + return; + } + + packet_header |= RxBroadcast; + +#if defined(DEBUG_RTL8139) + printf(">>> RTL8139: broadcast packet received\n"); +#endif + } else if (buf[0] & 0x01) { + /* multicast */ + if (!(s->RxConfig & AcceptMulticast)) + { +#if defined(DEBUG_RTL8139) + printf(">>> RTL8139: multicast packet rejected\n"); +#endif + return; + } + + int mcast_idx = compute_mcast_idx(buf); + + if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) + { +#if defined(DEBUG_RTL8139) + printf(">>> RTL8139: multicast address mismatch\n"); +#endif + return; + } + + packet_header |= RxMulticast; + +#if defined(DEBUG_RTL8139) + printf(">>> RTL8139: multicast packet received\n"); +#endif + } else if (s->phys[0] == buf[0] && + s->phys[1] == buf[1] && + s->phys[2] == buf[2] && + s->phys[3] == buf[3] && + s->phys[4] == buf[4] && + s->phys[5] == buf[5]) { + /* match */ + if (!(s->RxConfig & AcceptMyPhys)) + { +#if defined(DEBUG_RTL8139) + printf(">>> RTL8139: rejecting physical address matching packet\n"); +#endif + return; + } + + packet_header |= RxPhysical; + +#if defined(DEBUG_RTL8139) + printf(">>> RTL8139: physical address matching packet received\n"); +#endif + + } else { + +#if defined(DEBUG_RTL8139) + printf(">>> RTL8139: unknown packet\n"); +#endif + return; + } + } + + /* if too small buffer, then expand it */ + if (size < MIN_BUF_SIZE) { + memcpy(buf1, buf, size); + memset(buf1 + size, 0, MIN_BUF_SIZE - size); + buf = buf1; + size = MIN_BUF_SIZE; + } + + if (rtl8139_cp_receiver_enabled(s)) + { +#if defined(DEBUG_RTL8139) + printf("RTL8139: in C+ Rx mode ================\n"); +#endif + + /* begin C+ receiver mode */ + +/* w0 ownership flag */ +#define CP_RX_OWN (1<<31) +/* w0 end of ring flag */ +#define CP_RX_EOR (1<<30) +/* w0 bits 0...12 : buffer size */ +#define CP_RX_BUFFER_SIZE_MASK ((1<<13) - 1) +/* w1 tag available flag */ +#define CP_RX_TAVA (1<<16) +/* w1 bits 0...15 : VLAN tag */ +#define CP_RX_VLAN_TAG_MASK ((1<<16) - 1) +/* w2 low 32bit of Rx buffer ptr */ +/* w3 high 32bit of Rx buffer ptr */ + + int descriptor = s->currCPlusRxDesc; + target_phys_addr_t cplus_rx_ring_desc; + + cplus_rx_ring_desc = rtl8139_addr64(s->RxRingAddrLO, s->RxRingAddrHI); + cplus_rx_ring_desc += 16 * descriptor; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ C+ mode reading RX descriptor %d from host memory at %08x %08x = 0x%8lx\n", + descriptor, s->RxRingAddrHI, s->RxRingAddrLO, cplus_rx_ring_desc); +#endif + + uint32_t val, rxdw0,rxdw1,rxbufLO,rxbufHI; + + cpu_physical_memory_read(cplus_rx_ring_desc, (uint8_t *)&val, 4); + rxdw0 = le32_to_cpu(val); + cpu_physical_memory_read(cplus_rx_ring_desc+4, (uint8_t *)&val, 4); + rxdw1 = le32_to_cpu(val); + cpu_physical_memory_read(cplus_rx_ring_desc+8, (uint8_t *)&val, 4); + rxbufLO = le32_to_cpu(val); + cpu_physical_memory_read(cplus_rx_ring_desc+12, (uint8_t *)&val, 4); + rxbufHI = le32_to_cpu(val); + +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ C+ mode RX descriptor %d %08x %08x %08x %08x\n", + descriptor, + rxdw0, rxdw1, rxbufLO, rxbufHI); +#endif + + if (!(rxdw0 & CP_RX_OWN)) + { +#if defined(DEBUG_RTL8139) + printf("RTL8139: C+ Rx mode : descriptor %d is owned by host\n", descriptor); +#endif + s->IntrStatus |= RxOverflow; + ++s->RxMissed; + rtl8139_update_irq(s); + return; + } + + uint32_t rx_space = rxdw0 & CP_RX_BUFFER_SIZE_MASK; + + if (size+4 > rx_space) + { +#if defined(DEBUG_RTL8139) + printf("RTL8139: C+ Rx mode : descriptor %d size %d received %d + 4\n", + descriptor, rx_space, size); +#endif + s->IntrStatus |= RxOverflow; + ++s->RxMissed; + rtl8139_update_irq(s); + return; + } + + target_phys_addr_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI); + + /* receive/copy to target memory */ + cpu_physical_memory_write( rx_addr, buf, size ); + + /* write checksum */ +#if defined (RTL8139_CALCULATE_RXCRC) + val = cpu_to_le32(crc32(~0, buf, size)); +#else + val = 0; +#endif + cpu_physical_memory_write( rx_addr+size, (uint8_t *)&val, 4); + +/* first segment of received packet flag */ +#define CP_RX_STATUS_FS (1<<29) +/* last segment of received packet flag */ +#define CP_RX_STATUS_LS (1<<28) +/* multicast packet flag */ +#define CP_RX_STATUS_MAR (1<<26) +/* physical-matching packet flag */ +#define CP_RX_STATUS_PAM (1<<25) +/* broadcast packet flag */ +#define CP_RX_STATUS_BAR (1<<24) +/* runt packet flag */ +#define CP_RX_STATUS_RUNT (1<<19) +/* crc error flag */ +#define CP_RX_STATUS_CRC (1<<18) +/* IP checksum error flag */ +#define CP_RX_STATUS_IPF (1<<15) +/* UDP checksum error flag */ +#define CP_RX_STATUS_UDPF (1<<14) +/* TCP checksum error flag */ +#define CP_RX_STATUS_TCPF (1<<13) + + /* transfer ownership to target */ + rxdw0 &= ~CP_RX_OWN; + + /* set first segment bit */ + rxdw0 |= CP_RX_STATUS_FS; + + /* set last segment bit */ + rxdw0 |= CP_RX_STATUS_LS; + + /* set received packet type flags */ + if (packet_header & RxBroadcast) + rxdw0 |= CP_RX_STATUS_BAR; + if (packet_header & RxMulticast) + rxdw0 |= CP_RX_STATUS_MAR; + if (packet_header & RxPhysical) + rxdw0 |= CP_RX_STATUS_PAM; + + /* set received size */ + rxdw0 &= ~CP_RX_BUFFER_SIZE_MASK; + rxdw0 |= (size+4); + + /* reset VLAN tag flag */ + rxdw1 &= ~CP_RX_TAVA; + + /* update ring data */ + val = cpu_to_le32(rxdw0); + cpu_physical_memory_write(cplus_rx_ring_desc, (uint8_t *)&val, 4); + val = cpu_to_le32(rxdw1); + cpu_physical_memory_write(cplus_rx_ring_desc+4, (uint8_t *)&val, 4); + + /* seek to next Rx descriptor */ + if (rxdw0 & CP_RX_EOR) + { + s->currCPlusRxDesc = 0; + } + else + { + ++s->currCPlusRxDesc; + } + +#if defined(DEBUG_RTL8139) + printf("RTL8139: done C+ Rx mode ----------------\n"); +#endif + + } + else + { +#if defined(DEBUG_RTL8139) + printf("RTL8139: in ring Rx mode ================\n"); +#endif + /* begin ring receiver mode */ + int avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, s->RxBufferSize); + + /* if receiver buffer is empty then avail == 0 */ + + if (avail != 0 && size + 8 >= avail) + { +#if defined(DEBUG_RTL8139) + printf("rx overflow: rx buffer length %d head 0x%04x read 0x%04x === available 0x%04x need 0x%04x\n", + s->RxBufferSize, s->RxBufAddr, s->RxBufPtr, avail, size + 8); +#endif + s->IntrStatus |= RxOverflow; + ++s->RxMissed; + rtl8139_update_irq(s); + return; + } + + packet_header |= RxStatusOK; + + packet_header |= (((size+4) << 16) & 0xffff0000); + + /* write header */ + uint32_t val = cpu_to_le32(packet_header); + + rtl8139_write_buffer(s, (uint8_t *)&val, 4); + + rtl8139_write_buffer(s, buf, size); + + /* write checksum */ +#if defined (RTL8139_CALCULATE_RXCRC) + val = cpu_to_le32(crc32(~0, buf, size)); +#else + val = 0; +#endif + + rtl8139_write_buffer(s, (uint8_t *)&val, 4); + + /* correct buffer write pointer */ + s->RxBufAddr = MOD2((s->RxBufAddr + 3) & ~0x3, s->RxBufferSize); + + /* now we can signal we have received something */ + +#if defined(DEBUG_RTL8139) + printf(" received: rx buffer length %d head 0x%04x read 0x%04x\n", + s->RxBufferSize, s->RxBufAddr, s->RxBufPtr); +#endif + + } + + s->IntrStatus |= RxOK; + rtl8139_update_irq(s); +} + +static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize) +{ + s->RxBufferSize = bufferSize; + s->RxBufPtr = 0; + s->RxBufAddr = 0; +} + +static void rtl8139_reset(RTL8139State *s) +{ + int i; + + /* restore MAC address */ + memcpy(s->phys, s->macaddr, 6); + + /* reset interrupt mask */ + s->IntrStatus = 0; + s->IntrMask = 0; + + rtl8139_update_irq(s); + + /* prepare eeprom */ + s->eeprom.contents[0] = 0x8129; + memcpy(&s->eeprom.contents[7], s->macaddr, 6); + + /* mark all status registers as owned by host */ + for (i = 0; i < 4; ++i) + { + s->TxStatus[i] = TxHostOwns; + } + + s->currTxDesc = 0; + s->currCPlusRxDesc = 0; + s->currCPlusTxDesc = 0; + + s->RxRingAddrLO = 0; + s->RxRingAddrHI = 0; + + s->RxBuf = 0; + + rtl8139_reset_rxring(s, 8192); + + /* ACK the reset */ + s->TxConfig = 0; + +#if 0 +// s->TxConfig |= HW_REVID(1, 0, 0, 0, 0, 0, 0); // RTL-8139 HasHltClk + s->clock_enabled = 0; +#else + s->TxConfig |= HW_REVID(1, 1, 1, 0, 1, 0, 0); // RTL-8139C HasLWake + s->clock_enabled = 1; +#endif + + s->bChipCmdState = CmdReset; /* RxBufEmpty bit is calculated on read from ChipCmd */; + + /* set initial state data */ + s->Config0 = 0x0; /* No boot ROM */ + s->Config1 = 0xC; /* IO mapped and MEM mapped registers available */ + s->Config3 = 0x1; /* fast back-to-back compatible */ + s->Config5 = 0x0; + + s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD; + + s->CpCmd = 0x0; /* reset C+ mode */ + +// s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation +// s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex + s->BasicModeCtrl = 0x1000; // autonegotiation + + s->BasicModeStatus = 0x7809; + //s->BasicModeStatus |= 0x0040; /* UTP medium */ + s->BasicModeStatus |= 0x0020; /* autonegotiation completed */ + s->BasicModeStatus |= 0x0004; /* link is up */ + + s->NWayAdvert = 0x05e1; /* all modes, full duplex */ + s->NWayLPAR = 0x05e1; /* all modes, full duplex */ + s->NWayExpansion = 0x0001; /* autonegotiation supported */ +} + +static void rtl8139_ChipCmd_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: ChipCmd write val=0x%08x\n", val); +#endif + + if (val & CmdReset) + { +#ifdef DEBUG_RTL8139 + printf("RTL8139: ChipCmd reset\n"); +#endif + rtl8139_reset(s); + } + if (val & CmdRxEnb) + { +#ifdef DEBUG_RTL8139 + printf("RTL8139: ChipCmd enable receiver\n"); +#endif + } + if (val & CmdTxEnb) + { +#ifdef DEBUG_RTL8139 + printf("RTL8139: ChipCmd enable transmitter\n"); +#endif + } + + /* mask unwriteable bits */ + val = SET_MASKED(val, 0xe3, s->bChipCmdState); + + /* Deassert reset pin before next read */ + val &= ~CmdReset; + + s->bChipCmdState = val; +} + +static int rtl8139_RxBufferEmpty(RTL8139State *s) +{ + int unread = MOD2(s->RxBufferSize + s->RxBufAddr - s->RxBufPtr, s->RxBufferSize); + + if (unread != 0) + { +#ifdef DEBUG_RTL8139 + printf("RTL8139: receiver buffer data available 0x%04x\n", unread); +#endif + return 0; + } + +#ifdef DEBUG_RTL8139 + printf("RTL8139: receiver buffer is empty\n"); +#endif + + return 1; +} + +static uint32_t rtl8139_ChipCmd_read(RTL8139State *s) +{ + uint32_t ret = s->bChipCmdState; + + if (rtl8139_RxBufferEmpty(s)) + ret |= RxBufEmpty; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: ChipCmd read val=0x%04x\n", ret); +#endif + + return ret; +} + +static void rtl8139_CpCmd_write(RTL8139State *s, uint32_t val) +{ + val &= 0xffff; + +#ifdef DEBUG_RTL8139 + printf("RTL8139C+ command register write(w) val=0x%04x\n", val); +#endif + + /* mask unwriteable bits */ + val = SET_MASKED(val, 0xff84, s->CpCmd); + + s->CpCmd = val; +} + +static uint32_t rtl8139_CpCmd_read(RTL8139State *s) +{ + uint32_t ret = s->CpCmd; + +#ifdef DEBUG_RTL8139 + printf("RTL8139C+ command register read(w) val=0x%04x\n", ret); +#endif + + return ret; +} + +int rtl8139_config_writeable(RTL8139State *s) +{ + if (s->Cfg9346 & Cfg9346_Unlock) + { + return 1; + } + +#ifdef DEBUG_RTL8139 + printf("RTL8139: Configuration registers are write-protected\n"); +#endif + + return 0; +} + +static void rtl8139_BasicModeCtrl_write(RTL8139State *s, uint32_t val) +{ + val &= 0xffff; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: BasicModeCtrl register write(w) val=0x%04x\n", val); +#endif + + /* mask unwriteable bits */ + uint32 mask = 0x4cff; + + if (1 || !rtl8139_config_writeable(s)) + { + /* Speed setting and autonegotiation enable bits are read-only */ + mask |= 0x3000; + /* Duplex mode setting is read-only */ + mask |= 0x0100; + } + + val = SET_MASKED(val, mask, s->BasicModeCtrl); + + s->BasicModeCtrl = val; +} + +static uint32_t rtl8139_BasicModeCtrl_read(RTL8139State *s) +{ + uint32_t ret = s->BasicModeCtrl; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: BasicModeCtrl register read(w) val=0x%04x\n", ret); +#endif + + return ret; +} + +static void rtl8139_BasicModeStatus_write(RTL8139State *s, uint32_t val) +{ + val &= 0xffff; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: BasicModeStatus register write(w) val=0x%04x\n", val); +#endif + + /* mask unwriteable bits */ + val = SET_MASKED(val, 0xff3f, s->BasicModeStatus); + + s->BasicModeStatus = val; +} + +static uint32_t rtl8139_BasicModeStatus_read(RTL8139State *s) +{ + uint32_t ret = s->BasicModeStatus; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: BasicModeStatus register read(w) val=0x%04x\n", ret); +#endif + + return ret; +} + +static void rtl8139_Cfg9346_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: Cfg9346 write val=0x%02x\n", val); +#endif + + /* mask unwriteable bits */ + val = SET_MASKED(val, 0x31, s->Cfg9346); + + uint32_t opmode = val & 0xc0; + uint32_t eeprom_val = val & 0xf; + + if (opmode == 0x80) { + /* eeprom access */ + int eecs = (eeprom_val & 0x08)?1:0; + int eesk = (eeprom_val & 0x04)?1:0; + int eedi = (eeprom_val & 0x02)?1:0; + prom9346_set_wire(s, eecs, eesk, eedi); + } else if (opmode == 0x40) { + /* Reset. */ + val = 0; + rtl8139_reset(s); + } + + s->Cfg9346 = val; +} + +static uint32_t rtl8139_Cfg9346_read(RTL8139State *s) +{ + uint32_t ret = s->Cfg9346; + + uint32_t opmode = ret & 0xc0; + + if (opmode == 0x80) + { + /* eeprom access */ + int eedo = prom9346_get_wire(s); + if (eedo) + { + ret |= 0x01; + } + else + { + ret &= ~0x01; + } + } + +#ifdef DEBUG_RTL8139 + printf("RTL8139: Cfg9346 read val=0x%02x\n", ret); +#endif + + return ret; +} + +static void rtl8139_Config0_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: Config0 write val=0x%02x\n", val); +#endif + + if (!rtl8139_config_writeable(s)) + return; + + /* mask unwriteable bits */ + val = SET_MASKED(val, 0xf8, s->Config0); + + s->Config0 = val; +} + +static uint32_t rtl8139_Config0_read(RTL8139State *s) +{ + uint32_t ret = s->Config0; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: Config0 read val=0x%02x\n", ret); +#endif + + return ret; +} + +static void rtl8139_Config1_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: Config1 write val=0x%02x\n", val); +#endif + + if (!rtl8139_config_writeable(s)) + return; + + /* mask unwriteable bits */ + val = SET_MASKED(val, 0xC, s->Config1); + + s->Config1 = val; +} + +static uint32_t rtl8139_Config1_read(RTL8139State *s) +{ + uint32_t ret = s->Config1; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: Config1 read val=0x%02x\n", ret); +#endif + + return ret; +} + +static void rtl8139_Config3_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: Config3 write val=0x%02x\n", val); +#endif + + if (!rtl8139_config_writeable(s)) + return; + + /* mask unwriteable bits */ + val = SET_MASKED(val, 0x8F, s->Config3); + + s->Config3 = val; +} + +static uint32_t rtl8139_Config3_read(RTL8139State *s) +{ + uint32_t ret = s->Config3; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: Config3 read val=0x%02x\n", ret); +#endif + + return ret; +} + +static void rtl8139_Config4_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: Config4 write val=0x%02x\n", val); +#endif + + if (!rtl8139_config_writeable(s)) + return; + + /* mask unwriteable bits */ + val = SET_MASKED(val, 0x0a, s->Config4); + + s->Config4 = val; +} + +static uint32_t rtl8139_Config4_read(RTL8139State *s) +{ + uint32_t ret = s->Config4; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: Config4 read val=0x%02x\n", ret); +#endif + + return ret; +} + +static void rtl8139_Config5_write(RTL8139State *s, uint32_t val) +{ + val &= 0xff; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: Config5 write val=0x%02x\n", val); +#endif + + /* mask unwriteable bits */ + val = SET_MASKED(val, 0x80, s->Config5); + + s->Config5 = val; +} + +static uint32_t rtl8139_Config5_read(RTL8139State *s) +{ + uint32_t ret = s->Config5; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: Config5 read val=0x%02x\n", ret); +#endif + + return ret; +} + +static void rtl8139_TxConfig_write(RTL8139State *s, uint32_t val) +{ + if (!rtl8139_transmitter_enabled(s)) + { +#ifdef DEBUG_RTL8139 + printf("RTL8139: transmitter disabled; no TxConfig write val=0x%08x\n", val); +#endif + return; + } + +#ifdef DEBUG_RTL8139 + printf("RTL8139: TxConfig write val=0x%08x\n", val); +#endif + + val = SET_MASKED(val, TxVersionMask | 0x8070f80f, s->TxConfig); + + s->TxConfig = val; +} + +static void rtl8139_TxConfig_writeb(RTL8139State *s, uint32_t val) +{ +#ifdef DEBUG_RTL8139 + printf("RTL8139C TxConfig via write(b) val=0x%02x\n", val); +#endif + uint32_t tc = s->TxConfig; + tc &= 0xFFFFFF00; + tc |= (val & 0x000000FF); + rtl8139_TxConfig_write(s, tc); +} + +static uint32_t rtl8139_TxConfig_read(RTL8139State *s) +{ + uint32_t ret = s->TxConfig; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: TxConfig read val=0x%04x\n", ret); +#endif + + return ret; +} + +static void rtl8139_RxConfig_write(RTL8139State *s, uint32_t val) +{ +#ifdef DEBUG_RTL8139 + printf("RTL8139: RxConfig write val=0x%08x\n", val); +#endif + + /* mask unwriteable bits */ + val = SET_MASKED(val, 0xf0fc0040, s->RxConfig); + + s->RxConfig = val; + + /* reset buffer size and read/write pointers */ + rtl8139_reset_rxring(s, 8192 << ((s->RxConfig >> 11) & 0x3)); + +#ifdef DEBUG_RTL8139 + printf("RTL8139: RxConfig write reset buffer size to %d\n", s->RxBufferSize); +#endif +} + +static uint32_t rtl8139_RxConfig_read(RTL8139State *s) +{ + uint32_t ret = s->RxConfig; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: RxConfig read val=0x%08x\n", ret); +#endif + + return ret; +} + +static int rtl8139_transmit_one(RTL8139State *s, int descriptor) +{ + if (!rtl8139_transmitter_enabled(s)) + { +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ cannot transmit from descriptor %d: transmitter disabled\n", descriptor); +#endif + return 0; + } + + if (s->TxStatus[descriptor] & TxHostOwns) + { +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ cannot transmit from descriptor %d: owned by host (%08x)\n", descriptor, s->TxStatus[descriptor]); +#endif + return 0; + } + +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ transmitting from descriptor %d\n", descriptor); +#endif + + int txsize = s->TxStatus[descriptor] & 0x1fff; + uint8_t txbuffer[0x2000]; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ transmit reading %d bytes from host memory at 0x%08x\n", txsize, s->TxAddr[descriptor]); +#endif + cpu_physical_memory_read(s->TxAddr[descriptor], txbuffer, txsize); + + qemu_send_packet(s->vc, txbuffer, txsize); + + /* Mark descriptor as transferred */ + s->TxStatus[descriptor] |= TxHostOwns; + s->TxStatus[descriptor] |= TxStatOK; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ transmitted %d bytes from descriptor %d\n", txsize, descriptor); +#endif + + /* update interrupt */ + s->IntrStatus |= TxOK; + rtl8139_update_irq(s); + + return 1; +} + +static int rtl8139_cplus_transmit_one(RTL8139State *s) +{ + if (!rtl8139_transmitter_enabled(s)) + { +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ C+ mode: transmitter disabled\n"); +#endif + return 0; + } + + if (!rtl8139_cp_transmitter_enabled(s)) + { +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ C+ mode: C+ transmitter disabled\n"); +#endif + return 0 ; + } + + int descriptor = s->currCPlusTxDesc; + + target_phys_addr_t cplus_tx_ring_desc = + rtl8139_addr64(s->TxAddr[0], s->TxAddr[1]); + + /* Normal priority ring */ + cplus_tx_ring_desc += 16 * descriptor; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ C+ mode reading TX descriptor %d from host memory at %08x0x%08x = 0x%8lx\n", + descriptor, s->TxAddr[1], s->TxAddr[0], cplus_tx_ring_desc); +#endif + + uint32_t val, txdw0,txdw1,txbufLO,txbufHI; + + cpu_physical_memory_read(cplus_tx_ring_desc, (uint8_t *)&val, 4); + txdw0 = le32_to_cpu(val); + cpu_physical_memory_read(cplus_tx_ring_desc+4, (uint8_t *)&val, 4); + txdw1 = le32_to_cpu(val); + cpu_physical_memory_read(cplus_tx_ring_desc+8, (uint8_t *)&val, 4); + txbufLO = le32_to_cpu(val); + cpu_physical_memory_read(cplus_tx_ring_desc+12, (uint8_t *)&val, 4); + txbufHI = le32_to_cpu(val); + +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ C+ mode TX descriptor %d %08x %08x %08x %08x\n", + descriptor, + txdw0, txdw1, txbufLO, txbufHI); +#endif + +/* w0 ownership flag */ +#define CP_TX_OWN (1<<31) +/* w0 end of ring flag */ +#define CP_TX_EOR (1<<30) +/* first segment of received packet flag */ +#define CP_TX_FS (1<<29) +/* last segment of received packet flag */ +#define CP_TX_LS (1<<28) +/* large send packet flag */ +#define CP_TX_LGSEN (1<<27) +/* IP checksum offload flag */ +#define CP_TX_IPCS (1<<18) +/* UDP checksum offload flag */ +#define CP_TX_UDPCS (1<<17) +/* TCP checksum offload flag */ +#define CP_TX_TCPCS (1<<16) + +/* w0 bits 0...15 : buffer size */ +#define CP_TX_BUFFER_SIZE (1<<16) +#define CP_TX_BUFFER_SIZE_MASK (CP_TX_BUFFER_SIZE - 1) +/* w1 tag available flag */ +#define CP_RX_TAGC (1<<17) +/* w1 bits 0...15 : VLAN tag */ +#define CP_TX_VLAN_TAG_MASK ((1<<16) - 1) +/* w2 low 32bit of Rx buffer ptr */ +/* w3 high 32bit of Rx buffer ptr */ + +/* set after transmission */ +/* FIFO underrun flag */ +#define CP_TX_STATUS_UNF (1<<25) +/* transmit error summary flag, valid if set any of three below */ +#define CP_TX_STATUS_TES (1<<23) +/* out-of-window collision flag */ +#define CP_TX_STATUS_OWC (1<<22) +/* link failure flag */ +#define CP_TX_STATUS_LNKF (1<<21) +/* excessive collisions flag */ +#define CP_TX_STATUS_EXC (1<<20) + + if (!(txdw0 & CP_TX_OWN)) + { +#if defined(DEBUG_RTL8139) + printf("RTL8139: C+ Tx mode : descriptor %d is owned by host\n", descriptor); +#endif + return 0 ; + } + +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ C+ Tx mode : transmitting from descriptor %d\n", descriptor); +#endif + + int txsize = txdw0 & CP_TX_BUFFER_SIZE_MASK; + target_phys_addr_t tx_addr = rtl8139_addr64(txbufLO, txbufHI); + + uint8_t txbuffer[CP_TX_BUFFER_SIZE]; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ C+ mode transmit reading %d bytes from host memory at 0x%08x\n", txsize, tx_addr); +#endif + cpu_physical_memory_read(tx_addr, txbuffer, txsize); + + /* transmit the packet */ + qemu_send_packet(s->vc, txbuffer, txsize); + + /* transfer ownership to target */ + txdw0 &= ~CP_RX_OWN; + + /* reset error indicator bits */ + txdw0 &= ~CP_TX_STATUS_UNF; + txdw0 &= ~CP_TX_STATUS_TES; + txdw0 &= ~CP_TX_STATUS_OWC; + txdw0 &= ~CP_TX_STATUS_LNKF; + txdw0 &= ~CP_TX_STATUS_EXC; + + /* update ring data */ + val = cpu_to_le32(txdw0); + cpu_physical_memory_write(cplus_tx_ring_desc, (uint8_t *)&val, 4); +// val = cpu_to_le32(txdw1); +// cpu_physical_memory_write(cplus_tx_ring_desc+4, &val, 4); + + /* seek to next Rx descriptor */ + if (txdw0 & CP_TX_EOR) + { + s->currCPlusTxDesc = 0; + } + else + { + ++s->currCPlusTxDesc; + } + +#ifdef DEBUG_RTL8139 + printf("RTL8139: +++ C+ mode transmitted %d bytes from descriptor %d\n", txsize, descriptor); +#endif + return 1; +} + +static void rtl8139_cplus_transmit(RTL8139State *s) +{ + int txcount = 0; + + while (rtl8139_cplus_transmit_one(s)) + { + ++txcount; + } + + /* Mark transfer completed */ + if (!txcount) + { +#ifdef DEBUG_RTL8139 + printf("RTL8139: C+ mode : transmitter queue stalled, current TxDesc = %d\n", s->currCPlusTxDesc); +#endif + } + else + { + /* update interrupt status */ + s->IntrStatus |= TxOK; + rtl8139_update_irq(s); + } +} + +static void rtl8139_transmit(RTL8139State *s) +{ + int descriptor = s->currTxDesc, txcount = 0; + + /*while*/ + if (rtl8139_transmit_one(s, descriptor)) + { + ++s->currTxDesc; + s->currTxDesc %= 4; + ++txcount; + } + + /* Mark transfer completed */ + if (!txcount) + { +#ifdef DEBUG_RTL8139 + printf("RTL8139: transmitter queue stalled, current TxDesc = %d\n", s->currTxDesc); +#endif + } +} + +static void rtl8139_TxStatus_write(RTL8139State *s, uint32_t txRegOffset, uint32_t val) +{ + + int descriptor = txRegOffset/4; +#ifdef DEBUG_RTL8139 + printf("RTL8139: TxStatus write offset=0x%x val=0x%08x descriptor=%d\n", txRegOffset, val, descriptor); +#endif + + /* mask only reserved bits */ + val &= ~0xff00c000; /* these bits are reset on write */ + val = SET_MASKED(val, 0x00c00000, s->TxStatus[descriptor]); + + s->TxStatus[descriptor] = val; + + /* attempt to start transmission */ + rtl8139_transmit(s); +} + +static uint32_t rtl8139_TxStatus_read(RTL8139State *s, uint32_t txRegOffset) +{ + uint32_t ret = s->TxStatus[txRegOffset/4]; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: TxStatus read offset=0x%x val=0x%08x\n", txRegOffset, ret); +#endif + + return ret; +} + +static uint16_t rtl8139_TSAD_read(RTL8139State *s) +{ + uint16_t ret = 0; + + /* Simulate TSAD, it is read only anyway */ + + ret = ((s->TxStatus[3] & TxStatOK )?TSAD_TOK3:0) + |((s->TxStatus[2] & TxStatOK )?TSAD_TOK2:0) + |((s->TxStatus[1] & TxStatOK )?TSAD_TOK1:0) + |((s->TxStatus[0] & TxStatOK )?TSAD_TOK0:0) + + |((s->TxStatus[3] & TxUnderrun)?TSAD_TUN3:0) + |((s->TxStatus[2] & TxUnderrun)?TSAD_TUN2:0) + |((s->TxStatus[1] & TxUnderrun)?TSAD_TUN1:0) + |((s->TxStatus[0] & TxUnderrun)?TSAD_TUN0:0) + + |((s->TxStatus[3] & TxAborted )?TSAD_TABT3:0) + |((s->TxStatus[2] & TxAborted )?TSAD_TABT2:0) + |((s->TxStatus[1] & TxAborted )?TSAD_TABT1:0) + |((s->TxStatus[0] & TxAborted )?TSAD_TABT0:0) + + |((s->TxStatus[3] & TxHostOwns )?TSAD_OWN3:0) + |((s->TxStatus[2] & TxHostOwns )?TSAD_OWN2:0) + |((s->TxStatus[1] & TxHostOwns )?TSAD_OWN1:0) + |((s->TxStatus[0] & TxHostOwns )?TSAD_OWN0:0) ; + + +#ifdef DEBUG_RTL8139 + printf("RTL8139: TSAD read val=0x%04x\n", ret); +#endif + + return ret; +} + +static uint16_t rtl8139_CSCR_read(RTL8139State *s) +{ + uint16_t ret = s->CSCR; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: CSCR read val=0x%04x\n", ret); +#endif + + return ret; +} + +static void rtl8139_TxAddr_write(RTL8139State *s, uint32_t txAddrOffset, uint32_t val) +{ +#ifdef DEBUG_RTL8139 + printf("RTL8139: TxAddr write offset=0x%x val=0x%08x\n", txAddrOffset, val); +#endif + + s->TxAddr[txAddrOffset/4] = le32_to_cpu(val); +} + +static uint32_t rtl8139_TxAddr_read(RTL8139State *s, uint32_t txAddrOffset) +{ + uint32_t ret = cpu_to_le32(s->TxAddr[txAddrOffset/4]); + +#ifdef DEBUG_RTL8139 + printf("RTL8139: TxAddr read offset=0x%x val=0x%08x\n", txAddrOffset, ret); +#endif + + return ret; +} + +static void rtl8139_RxBufPtr_write(RTL8139State *s, uint32_t val) +{ +#ifdef DEBUG_RTL8139 + printf("RTL8139: RxBufPtr write val=0x%04x\n", val); +#endif + + /* this value is off by 16 */ + s->RxBufPtr = MOD2(val + 0x10, s->RxBufferSize); + +#if defined(DEBUG_RTL8139) + printf(" CAPR write: rx buffer length %d head 0x%04x read 0x%04x\n", + s->RxBufferSize, s->RxBufAddr, s->RxBufPtr); +#endif +} + +static uint32_t rtl8139_RxBufPtr_read(RTL8139State *s) +{ + /* this value is off by 16 */ + uint32_t ret = s->RxBufPtr - 0x10; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: RxBufPtr read val=0x%04x\n", ret); +#endif + + return ret; +} + +static void rtl8139_RxBuf_write(RTL8139State *s, uint32_t val) +{ +#ifdef DEBUG_RTL8139 + printf("RTL8139: RxBuf write val=0x%08x\n", val); +#endif + + s->RxBuf = val; + + /* may need to reset rxring here */ +} + +static uint32_t rtl8139_RxBuf_read(RTL8139State *s) +{ + uint32_t ret = s->RxBuf; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: RxBuf read val=0x%08x\n", ret); +#endif + + return ret; +} + +static void rtl8139_IntrMask_write(RTL8139State *s, uint32_t val) +{ +#ifdef DEBUG_RTL8139 + printf("RTL8139: IntrMask write(w) val=0x%04x\n", val); +#endif + + /* mask unwriteable bits */ + val = SET_MASKED(val, 0x1e00, s->IntrMask); + + s->IntrMask = val; + + rtl8139_update_irq(s); +} + +static uint32_t rtl8139_IntrMask_read(RTL8139State *s) +{ + uint32_t ret = s->IntrMask; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: IntrMask read(w) val=0x%04x\n", ret); +#endif + + return ret; +} + +static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val) +{ +#ifdef DEBUG_RTL8139 + printf("RTL8139: IntrStatus write(w) val=0x%04x\n", val); +#endif + +#if 0 + + /* writing to ISR has no effect */ + + return; + +#else + uint16_t newStatus = s->IntrStatus & ~val; + + /* mask unwriteable bits */ + newStatus = SET_MASKED(newStatus, 0x1e00, s->IntrStatus); + + /* writing 1 to interrupt status register bit clears it */ + s->IntrStatus = 0; + rtl8139_update_irq(s); + + s->IntrStatus = newStatus; + rtl8139_update_irq(s); +#endif +} + +static uint32_t rtl8139_IntrStatus_read(RTL8139State *s) +{ + uint32_t ret = s->IntrStatus; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: IntrStatus read(w) val=0x%04x\n", ret); +#endif + +#if 0 + + /* reading ISR clears all interrupts */ + s->IntrStatus = 0; + + rtl8139_update_irq(s); + +#endif + + return ret; +} + +static void rtl8139_MultiIntr_write(RTL8139State *s, uint32_t val) +{ +#ifdef DEBUG_RTL8139 + printf("RTL8139: MultiIntr write(w) val=0x%04x\n", val); +#endif + + /* mask unwriteable bits */ + val = SET_MASKED(val, 0xf000, s->MultiIntr); + + s->MultiIntr = val; +} + +static uint32_t rtl8139_MultiIntr_read(RTL8139State *s) +{ + uint32_t ret = s->MultiIntr; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: MultiIntr read(w) val=0x%04x\n", ret); +#endif + + return ret; +} + +static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val) +{ + RTL8139State *s = opaque; + + addr &= 0xff; + + switch (addr) + { + case MAC0 ... MAC0+5: + s->phys[addr - MAC0] = val; + break; + case MAC0+6 ... MAC0+7: + /* reserved */ + break; + case MAR0 ... MAR0+7: + s->mult[addr - MAR0] = val; + break; + case ChipCmd: + rtl8139_ChipCmd_write(s, val); + break; + case Cfg9346: + rtl8139_Cfg9346_write(s, val); + break; + case TxConfig: /* windows driver sometimes writes using byte-lenth call */ + rtl8139_TxConfig_writeb(s, val); + break; + case Config0: + rtl8139_Config0_write(s, val); + break; + case Config1: + rtl8139_Config1_write(s, val); + break; + case Config3: + rtl8139_Config3_write(s, val); + break; + case Config4: + rtl8139_Config4_write(s, val); + break; + case Config5: + rtl8139_Config5_write(s, val); + break; + case MediaStatus: + /* ignore */ +#ifdef DEBUG_RTL8139 + printf("RTL8139: not implemented write(b) to MediaStatus val=0x%02x\n", val); +#endif + break; + + case HltClk: +#ifdef DEBUG_RTL8139 + printf("RTL8139: HltClk write val=0x%08x\n", val); +#endif + if (val == 'R') + { + s->clock_enabled = 1; + } + else if (val == 'H') + { + s->clock_enabled = 0; + } + break; + + case TxThresh: +#ifdef DEBUG_RTL8139 + printf("RTL8139C+ TxThresh write(b) val=0x%02x\n", val); +#endif + s->TxThresh = val; + break; + + case TxPoll: +#ifdef DEBUG_RTL8139 + printf("RTL8139C+ TxPoll write(b) val=0x%02x\n", val); +#endif + if (val & (1 << 7)) + { +#ifdef DEBUG_RTL8139 + printf("RTL8139C+ TxPoll high priority transmission (not implemented)\n"); +#endif + //rtl8139_cplus_transmit(s); + } + if (val & (1 << 6)) + { +#ifdef DEBUG_RTL8139 + printf("RTL8139C+ TxPoll normal priority transmission\n"); +#endif + rtl8139_cplus_transmit(s); + } + + break; + + default: +#ifdef DEBUG_RTL8139 + printf("RTL8139: not implemented write(b) addr=0x%x val=0x%02x\n", addr, val); +#endif + break; + } +} + +static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val) +{ + RTL8139State *s = opaque; + + addr &= 0xfe; + + switch (addr) + { + case IntrMask: + rtl8139_IntrMask_write(s, val); + break; + + case IntrStatus: + rtl8139_IntrStatus_write(s, val); + break; + + case MultiIntr: + rtl8139_MultiIntr_write(s, val); + break; + + case RxBufPtr: + rtl8139_RxBufPtr_write(s, val); + break; + + case BasicModeCtrl: + rtl8139_BasicModeCtrl_write(s, val); + break; + case BasicModeStatus: + rtl8139_BasicModeStatus_write(s, val); + break; + case NWayAdvert: +#ifdef DEBUG_RTL8139 + printf("RTL8139: NWayAdvert write(w) val=0x%04x\n", val); +#endif + s->NWayAdvert = val; + break; + case NWayLPAR: +#ifdef DEBUG_RTL8139 + printf("RTL8139: forbidden NWayLPAR write(w) val=0x%04x\n", val); +#endif + break; + case NWayExpansion: +#ifdef DEBUG_RTL8139 + printf("RTL8139: NWayExpansion write(w) val=0x%04x\n", val); +#endif + s->NWayExpansion = val; + break; + + case CpCmd: + rtl8139_CpCmd_write(s, val); + break; + + default: +#ifdef DEBUG_RTL8139 + printf("RTL8139: ioport write(w) addr=0x%x val=0x%04x via write(b)\n", addr, val); +#endif + +#ifdef TARGET_WORDS_BIGENDIAN + rtl8139_io_writeb(opaque, addr, (val >> 8) & 0xff); + rtl8139_io_writeb(opaque, addr + 1, val & 0xff); +#else + rtl8139_io_writeb(opaque, addr, val & 0xff); + rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff); +#endif + break; + } +} + +static void rtl8139_io_writel(void *opaque, uint8_t addr, uint32_t val) +{ + RTL8139State *s = opaque; + + addr &= 0xfc; + + switch (addr) + { + case RxMissed: +#ifdef DEBUG_RTL8139 + printf("RTL8139: RxMissed clearing on write\n"); +#endif + s->RxMissed = 0; + break; + + case TxConfig: + rtl8139_TxConfig_write(s, val); + break; + + case RxConfig: + rtl8139_RxConfig_write(s, val); + break; + + case TxStatus0 ... TxStatus0+4*4-1: + rtl8139_TxStatus_write(s, addr-TxStatus0, val); + break; + + case TxAddr0 ... TxAddr0+4*4-1: + rtl8139_TxAddr_write(s, addr-TxAddr0, val); + break; + + case RxBuf: + rtl8139_RxBuf_write(s, val); + break; + + case RxRingAddrLO: +#ifdef DEBUG_RTL8139 + printf("RTL8139: C+ RxRing low bits write val=0x%08x\n", val); +#endif + s->RxRingAddrLO = val; + break; + + case RxRingAddrHI: +#ifdef DEBUG_RTL8139 + printf("RTL8139: C+ RxRing high bits write val=0x%08x\n", val); +#endif + s->RxRingAddrHI = val; + break; + + default: +#ifdef DEBUG_RTL8139 + printf("RTL8139: ioport write(l) addr=0x%x val=0x%08x via write(b)\n", addr, val); +#endif +#ifdef TARGET_WORDS_BIGENDIAN + rtl8139_io_writeb(opaque, addr, (val >> 24) & 0xff); + rtl8139_io_writeb(opaque, addr + 1, (val >> 16) & 0xff); + rtl8139_io_writeb(opaque, addr + 2, (val >> 8) & 0xff); + rtl8139_io_writeb(opaque, addr + 3, val & 0xff); +#else + rtl8139_io_writeb(opaque, addr, val & 0xff); + rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff); + rtl8139_io_writeb(opaque, addr + 2, (val >> 16) & 0xff); + rtl8139_io_writeb(opaque, addr + 3, (val >> 24) & 0xff); +#endif + break; + } +} + +static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr) +{ + RTL8139State *s = opaque; + int ret; + + addr &= 0xff; + + switch (addr) + { + case MAC0 ... MAC0+5: + ret = s->phys[addr - MAC0]; + break; + case MAC0+6 ... MAC0+7: + ret = 0; + break; + case MAR0 ... MAR0+7: + ret = s->mult[addr - MAR0]; + break; + case ChipCmd: + ret = rtl8139_ChipCmd_read(s); + break; + case Cfg9346: + ret = rtl8139_Cfg9346_read(s); + break; + case Config0: + ret = rtl8139_Config0_read(s); + break; + case Config1: + ret = rtl8139_Config1_read(s); + break; + case Config3: + ret = rtl8139_Config3_read(s); + break; + case Config4: + ret = rtl8139_Config4_read(s); + break; + case Config5: + ret = rtl8139_Config5_read(s); + break; + + case MediaStatus: + ret = 0xd0; +#ifdef DEBUG_RTL8139 + printf("RTL8139: MediaStatus read 0x%x\n", ret); +#endif + break; + + case HltClk: + ret = s->clock_enabled; +#ifdef DEBUG_RTL8139 + printf("RTL8139: HltClk read 0x%x\n", ret); +#endif + break; + + case PCIRevisionID: + ret = 0x10; +#ifdef DEBUG_RTL8139 + printf("RTL8139: PCI Revision ID read 0x%x\n", ret); +#endif + break; + + case TxThresh: + ret = s->TxThresh; +#ifdef DEBUG_RTL8139 + printf("RTL8139C+ TxThresh read(b) val=0x%02x\n", ret); +#endif + break; + + case 0x43: /* Part of TxConfig register. Windows driver tries to read it */ + ret = s->TxConfig >> 24; +#ifdef DEBUG_RTL8139 + printf("RTL8139C TxConfig at 0x43 read(b) val=0x%02x\n", ret); +#endif + break; + + default: +#ifdef DEBUG_RTL8139 + printf("RTL8139: not implemented read(b) addr=0x%x\n", addr); +#endif + ret = 0; + break; + } + + return ret; +} + +static uint32_t rtl8139_io_readw(void *opaque, uint8_t addr) +{ + RTL8139State *s = opaque; + uint32_t ret; + + addr &= 0xfe; /* mask lower bit */ + + switch (addr) + { + case IntrMask: + ret = rtl8139_IntrMask_read(s); + break; + + case IntrStatus: + ret = rtl8139_IntrStatus_read(s); + break; + + case MultiIntr: + ret = rtl8139_MultiIntr_read(s); + break; + + case RxBufPtr: + ret = rtl8139_RxBufPtr_read(s); + break; + + case BasicModeCtrl: + ret = rtl8139_BasicModeCtrl_read(s); + break; + case BasicModeStatus: + ret = rtl8139_BasicModeStatus_read(s); + break; + case NWayAdvert: + ret = s->NWayAdvert; +#ifdef DEBUG_RTL8139 + printf("RTL8139: NWayAdvert read(w) val=0x%04x\n", ret); +#endif + break; + case NWayLPAR: + ret = s->NWayLPAR; +#ifdef DEBUG_RTL8139 + printf("RTL8139: NWayLPAR read(w) val=0x%04x\n", ret); +#endif + break; + case NWayExpansion: + ret = s->NWayExpansion; +#ifdef DEBUG_RTL8139 + printf("RTL8139: NWayExpansion read(w) val=0x%04x\n", ret); +#endif + break; + + case CpCmd: + ret = rtl8139_CpCmd_read(s); + break; + + case TxSummary: + ret = rtl8139_TSAD_read(s); + break; + + case CSCR: + ret = rtl8139_CSCR_read(s); + break; + + default: +#ifdef DEBUG_RTL8139 + printf("RTL8139: ioport read(w) addr=0x%x via read(b)\n", addr); +#endif + +#ifdef TARGET_WORDS_BIGENDIAN + ret = rtl8139_io_readb(opaque, addr) << 8; + ret |= rtl8139_io_readb(opaque, addr + 1); +#else + ret = rtl8139_io_readb(opaque, addr); + ret |= rtl8139_io_readb(opaque, addr + 1) << 8; +#endif + +#ifdef DEBUG_RTL8139 + printf("RTL8139: ioport read(w) addr=0x%x val=0x%04x\n", addr, ret); +#endif + break; + } + + return ret; +} + +static uint32_t rtl8139_io_readl(void *opaque, uint8_t addr) +{ + RTL8139State *s = opaque; + uint32_t ret; + + addr &= 0xfc; /* also mask low 2 bits */ + + switch (addr) + { + case RxMissed: + ret = s->RxMissed; + +#ifdef DEBUG_RTL8139 + printf("RTL8139: RxMissed read val=0x%08x\n", ret); +#endif + break; + + case TxConfig: + ret = rtl8139_TxConfig_read(s); + break; + + case RxConfig: + ret = rtl8139_RxConfig_read(s); + break; + + case TxStatus0 ... TxStatus0+4*4-1: + ret = rtl8139_TxStatus_read(s, addr-TxStatus0); + break; + + case TxAddr0 ... TxAddr0+4*4-1: + ret = rtl8139_TxAddr_read(s, addr-TxAddr0); + break; + + case RxBuf: + ret = rtl8139_RxBuf_read(s); + break; + + case RxRingAddrLO: + ret = s->RxRingAddrLO; +#ifdef DEBUG_RTL8139 + printf("RTL8139: C+ RxRing low bits read val=0x%08x\n", ret); +#endif + break; + + case RxRingAddrHI: + ret = s->RxRingAddrHI; +#ifdef DEBUG_RTL8139 + printf("RTL8139: C+ RxRing high bits read val=0x%08x\n", ret); +#endif + break; + + default: +#ifdef DEBUG_RTL8139 + printf("RTL8139: ioport read(l) addr=0x%x via read(b)\n", addr); +#endif + +#ifdef TARGET_WORDS_BIGENDIAN + ret = rtl8139_io_readb(opaque, addr) << 24; + ret |= rtl8139_io_readb(opaque, addr + 1) << 16; + ret |= rtl8139_io_readb(opaque, addr + 2) << 8; + ret |= rtl8139_io_readb(opaque, addr + 3); +#else + ret = rtl8139_io_readb(opaque, addr); + ret |= rtl8139_io_readb(opaque, addr + 1) << 8; + ret |= rtl8139_io_readb(opaque, addr + 2) << 16; + ret |= rtl8139_io_readb(opaque, addr + 3) << 24; +#endif + +#ifdef DEBUG_RTL8139 + printf("RTL8139: read(l) addr=0x%x val=%08x\n", addr, ret); +#endif + break; + } + + return ret; +} + +/* */ + +static void rtl8139_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + rtl8139_io_writeb(opaque, addr & 0xFF, val); +} + +static void rtl8139_ioport_writew(void *opaque, uint32_t addr, uint32_t val) +{ + rtl8139_io_writew(opaque, addr & 0xFF, val); +} + +static void rtl8139_ioport_writel(void *opaque, uint32_t addr, uint32_t val) +{ + rtl8139_io_writel(opaque, addr & 0xFF, val); +} + +static uint32_t rtl8139_ioport_readb(void *opaque, uint32_t addr) +{ + return rtl8139_io_readb(opaque, addr & 0xFF); +} + +static uint32_t rtl8139_ioport_readw(void *opaque, uint32_t addr) +{ + return rtl8139_io_readw(opaque, addr & 0xFF); +} + +static uint32_t rtl8139_ioport_readl(void *opaque, uint32_t addr) +{ + return rtl8139_io_readl(opaque, addr & 0xFF); +} + +/* */ + +static void rtl8139_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + rtl8139_io_writeb(opaque, addr & 0xFF, val); +} + +static void rtl8139_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + rtl8139_io_writew(opaque, addr & 0xFF, val); +} + +static void rtl8139_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + rtl8139_io_writel(opaque, addr & 0xFF, val); +} + +static uint32_t rtl8139_mmio_readb(void *opaque, target_phys_addr_t addr) +{ + return rtl8139_io_readb(opaque, addr & 0xFF); +} + +static uint32_t rtl8139_mmio_readw(void *opaque, target_phys_addr_t addr) +{ + return rtl8139_io_readw(opaque, addr & 0xFF); +} + +static uint32_t rtl8139_mmio_readl(void *opaque, target_phys_addr_t addr) +{ + return rtl8139_io_readl(opaque, addr & 0xFF); +} + +/* */ + +static void rtl8139_save(QEMUFile* f,void* opaque) +{ + RTL8139State* s=(RTL8139State*)opaque; + int i; + + qemu_put_buffer(f, s->phys, 6); + qemu_put_buffer(f, s->mult, 8); + + for (i=0; i<4; ++i) + { + qemu_put_be32s(f, &s->TxStatus[i]); /* TxStatus0 */ + } + for (i=0; i<4; ++i) + { + qemu_put_be32s(f, &s->TxAddr[i]); /* TxAddr0 */ + } + + qemu_put_be32s(f, &s->RxBuf); /* Receive buffer */ + qemu_put_be32s(f, &s->RxBufferSize);/* internal variable, receive ring buffer size in C mode */ + qemu_put_be32s(f, &s->RxBufPtr); + qemu_put_be32s(f, &s->RxBufAddr); + + qemu_put_be16s(f, &s->IntrStatus); + qemu_put_be16s(f, &s->IntrMask); + + qemu_put_be32s(f, &s->TxConfig); + qemu_put_be32s(f, &s->RxConfig); + qemu_put_be32s(f, &s->RxMissed); + qemu_put_be16s(f, &s->CSCR); + + qemu_put_8s(f, &s->Cfg9346); + qemu_put_8s(f, &s->Config0); + qemu_put_8s(f, &s->Config1); + qemu_put_8s(f, &s->Config3); + qemu_put_8s(f, &s->Config4); + qemu_put_8s(f, &s->Config5); + + qemu_put_8s(f, &s->clock_enabled); + qemu_put_8s(f, &s->bChipCmdState); + + qemu_put_be16s(f, &s->MultiIntr); + + qemu_put_be16s(f, &s->BasicModeCtrl); + qemu_put_be16s(f, &s->BasicModeStatus); + qemu_put_be16s(f, &s->NWayAdvert); + qemu_put_be16s(f, &s->NWayLPAR); + qemu_put_be16s(f, &s->NWayExpansion); + + qemu_put_be16s(f, &s->CpCmd); + qemu_put_8s(f, &s->TxThresh); + + qemu_put_be32s(f, &s->irq); + qemu_put_buffer(f, s->macaddr, 6); + qemu_put_be32s(f, &s->rtl8139_mmio_io_addr); + + qemu_put_be32s(f, &s->currTxDesc); + qemu_put_be32s(f, &s->currCPlusRxDesc); + qemu_put_be32s(f, &s->currCPlusTxDesc); + qemu_put_be32s(f, &s->RxRingAddrLO); + qemu_put_be32s(f, &s->RxRingAddrHI); + + for (i=0; ieeprom.contents[i]); + } + qemu_put_be32s(f, &s->eeprom.mode); + qemu_put_be32s(f, &s->eeprom.tick); + qemu_put_8s(f, &s->eeprom.address); + qemu_put_be16s(f, &s->eeprom.input); + qemu_put_be16s(f, &s->eeprom.output); + + qemu_put_8s(f, &s->eeprom.eecs); + qemu_put_8s(f, &s->eeprom.eesk); + qemu_put_8s(f, &s->eeprom.eedi); + qemu_put_8s(f, &s->eeprom.eedo); +} + +static int rtl8139_load(QEMUFile* f,void* opaque,int version_id) +{ + RTL8139State* s=(RTL8139State*)opaque; + int i; + + if (version_id != 1) + return -EINVAL; + + qemu_get_buffer(f, s->phys, 6); + qemu_get_buffer(f, s->mult, 8); + + for (i=0; i<4; ++i) + { + qemu_get_be32s(f, &s->TxStatus[i]); /* TxStatus0 */ + } + for (i=0; i<4; ++i) + { + qemu_get_be32s(f, &s->TxAddr[i]); /* TxAddr0 */ + } + + qemu_get_be32s(f, &s->RxBuf); /* Receive buffer */ + qemu_get_be32s(f, &s->RxBufferSize);/* internal variable, receive ring buffer size in C mode */ + qemu_get_be32s(f, &s->RxBufPtr); + qemu_get_be32s(f, &s->RxBufAddr); + + qemu_get_be16s(f, &s->IntrStatus); + qemu_get_be16s(f, &s->IntrMask); + + qemu_get_be32s(f, &s->TxConfig); + qemu_get_be32s(f, &s->RxConfig); + qemu_get_be32s(f, &s->RxMissed); + qemu_get_be16s(f, &s->CSCR); + + qemu_get_8s(f, &s->Cfg9346); + qemu_get_8s(f, &s->Config0); + qemu_get_8s(f, &s->Config1); + qemu_get_8s(f, &s->Config3); + qemu_get_8s(f, &s->Config4); + qemu_get_8s(f, &s->Config5); + + qemu_get_8s(f, &s->clock_enabled); + qemu_get_8s(f, &s->bChipCmdState); + + qemu_get_be16s(f, &s->MultiIntr); + + qemu_get_be16s(f, &s->BasicModeCtrl); + qemu_get_be16s(f, &s->BasicModeStatus); + qemu_get_be16s(f, &s->NWayAdvert); + qemu_get_be16s(f, &s->NWayLPAR); + qemu_get_be16s(f, &s->NWayExpansion); + + qemu_get_be16s(f, &s->CpCmd); + qemu_get_8s(f, &s->TxThresh); + + qemu_get_be32s(f, &s->irq); + qemu_get_buffer(f, s->macaddr, 6); + qemu_get_be32s(f, &s->rtl8139_mmio_io_addr); + + qemu_get_be32s(f, &s->currTxDesc); + qemu_get_be32s(f, &s->currCPlusRxDesc); + qemu_get_be32s(f, &s->currCPlusTxDesc); + qemu_get_be32s(f, &s->RxRingAddrLO); + qemu_get_be32s(f, &s->RxRingAddrHI); + + for (i=0; ieeprom.contents[i]); + } + qemu_get_be32s(f, &s->eeprom.mode); + qemu_get_be32s(f, &s->eeprom.tick); + qemu_get_8s(f, &s->eeprom.address); + qemu_get_be16s(f, &s->eeprom.input); + qemu_get_be16s(f, &s->eeprom.output); + + qemu_get_8s(f, &s->eeprom.eecs); + qemu_get_8s(f, &s->eeprom.eesk); + qemu_get_8s(f, &s->eeprom.eedi); + qemu_get_8s(f, &s->eeprom.eedo); + + return 0; +} + +/***********************************************************/ +/* PCI RTL8139 definitions */ + +typedef struct PCIRTL8139State { + PCIDevice dev; + RTL8139State rtl8139; +} PCIRTL8139State; + +static void rtl8139_mmio_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + PCIRTL8139State *d = (PCIRTL8139State *)pci_dev; + RTL8139State *s = &d->rtl8139; + + cpu_register_physical_memory(addr + 0, 0x100, s->rtl8139_mmio_io_addr); +} + +static void rtl8139_ioport_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + PCIRTL8139State *d = (PCIRTL8139State *)pci_dev; + RTL8139State *s = &d->rtl8139; + + register_ioport_write(addr, 0x100, 1, rtl8139_ioport_writeb, s); + register_ioport_read( addr, 0x100, 1, rtl8139_ioport_readb, s); + + register_ioport_write(addr, 0x100, 2, rtl8139_ioport_writew, s); + register_ioport_read( addr, 0x100, 2, rtl8139_ioport_readw, s); + + register_ioport_write(addr, 0x100, 4, rtl8139_ioport_writel, s); + register_ioport_read( addr, 0x100, 4, rtl8139_ioport_readl, s); +} + +static CPUReadMemoryFunc *rtl8139_mmio_read[3] = { + rtl8139_mmio_readb, + rtl8139_mmio_readw, + rtl8139_mmio_readl, +}; + +static CPUWriteMemoryFunc *rtl8139_mmio_write[3] = { + rtl8139_mmio_writeb, + rtl8139_mmio_writew, + rtl8139_mmio_writel, +}; + +void pci_rtl8139_init(PCIBus *bus, NICInfo *nd) +{ + PCIRTL8139State *d; + RTL8139State *s; + uint8_t *pci_conf; + + d = (PCIRTL8139State *)pci_register_device(bus, + "RTL8139", sizeof(PCIRTL8139State), + -1, + NULL, NULL); + pci_conf = d->dev.config; + pci_conf[0x00] = 0xec; /* Realtek 8139 */ + pci_conf[0x01] = 0x10; + pci_conf[0x02] = 0x39; + pci_conf[0x03] = 0x81; + pci_conf[0x04] = 0x05; /* command = I/O space, Bus Master */ + pci_conf[0x08] = 0x20; /* 0x10 */ /* PCI revision ID; >=0x20 is for 8139C+ */ + pci_conf[0x0a] = 0x00; /* ethernet network controller */ + pci_conf[0x0b] = 0x02; + pci_conf[0x0e] = 0x00; /* header_type */ + pci_conf[0x3d] = 1; /* interrupt pin 0 */ + pci_conf[0x34] = 0xdc; + + s = &d->rtl8139; + + /* I/O handler for memory-mapped I/O */ + s->rtl8139_mmio_io_addr = + cpu_register_io_memory(0, rtl8139_mmio_read, rtl8139_mmio_write, s); + + pci_register_io_region(&d->dev, 0, 0x100, + PCI_ADDRESS_SPACE_IO, rtl8139_ioport_map); + + pci_register_io_region(&d->dev, 1, 0x100, + PCI_ADDRESS_SPACE_MEM, rtl8139_mmio_map); + + s->irq = 16; /* PCI interrupt */ + s->pci_dev = (PCIDevice *)d; + memcpy(s->macaddr, nd->macaddr, 6); + rtl8139_reset(s); + s->vc = qemu_new_vlan_client(nd->vlan, rtl8139_receive, + rtl8139_can_receive, s); + + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "rtl8139 pci macaddr=%02x:%02x:%02x:%02x:%02x:%02x", + s->macaddr[0], + s->macaddr[1], + s->macaddr[2], + s->macaddr[3], + s->macaddr[4], + s->macaddr[5]); + + /* XXX: instance number ? */ + register_savevm("rtl8139", 0, 1, rtl8139_save, rtl8139_load, s); + register_savevm("rtl8139_pci", 0, 1, generic_pci_save, generic_pci_load, + &d->dev); +} diff --git a/tools/ioemu/hw/sb16.c b/tools/ioemu/hw/sb16.c new file mode 100644 index 0000000000..f7b12e6116 --- /dev/null +++ b/tools/ioemu/hw/sb16.c @@ -0,0 +1,1415 @@ +/* + * QEMU Soundblaster 16 emulation + * + * Copyright (c) 2003-2005 Vassili Karpov (malc) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#define LENOFA(a) ((int) (sizeof(a)/sizeof(a[0]))) + +#define dolog(...) AUD_log ("sb16", __VA_ARGS__) + +/* #define DEBUG */ +/* #define DEBUG_SB16_MOST */ + +#ifdef DEBUG +#define ldebug(...) dolog (__VA_ARGS__) +#else +#define ldebug(...) +#endif + +#define IO_READ_PROTO(name) \ + uint32_t name (void *opaque, uint32_t nport) +#define IO_WRITE_PROTO(name) \ + void name (void *opaque, uint32_t nport, uint32_t val) + +static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992."; + +static struct { + int ver_lo; + int ver_hi; + int irq; + int dma; + int hdma; + int port; +} conf = {5, 4, 5, 1, 5, 0x220}; + +typedef struct SB16State { + QEMUSoundCard card; + int irq; + int dma; + int hdma; + int port; + int ver; + + int in_index; + int out_data_len; + int fmt_stereo; + int fmt_signed; + int fmt_bits; + audfmt_e fmt; + int dma_auto; + int block_size; + int fifo; + int freq; + int time_const; + int speaker; + int needed_bytes; + int cmd; + int use_hdma; + int highspeed; + int can_write; + + int v2x6; + + uint8_t csp_param; + uint8_t csp_value; + uint8_t csp_mode; + uint8_t csp_regs[256]; + uint8_t csp_index; + uint8_t csp_reg83[4]; + int csp_reg83r; + int csp_reg83w; + + uint8_t in2_data[10]; + uint8_t out_data[50]; + uint8_t test_reg; + uint8_t last_read_byte; + int nzero; + + int left_till_irq; + + int dma_running; + int bytes_per_second; + int align; + int audio_free; + SWVoiceOut *voice; + + QEMUTimer *aux_ts; + /* mixer state */ + int mixer_nreg; + uint8_t mixer_regs[256]; +} SB16State; + +static void SB_audio_callback (void *opaque, int free); + +static int magic_of_irq (int irq) +{ + switch (irq) { + case 5: + return 2; + case 7: + return 4; + case 9: + return 1; + case 10: + return 8; + default: + dolog ("bad irq %d\n", irq); + return 2; + } +} + +static int irq_of_magic (int magic) +{ + switch (magic) { + case 1: + return 9; + case 2: + return 5; + case 4: + return 7; + case 8: + return 10; + default: + dolog ("bad irq magic %d\n", magic); + return -1; + } +} + +#if 0 +static void log_dsp (SB16State *dsp) +{ + ldebug ("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n", + dsp->fmt_stereo ? "Stereo" : "Mono", + dsp->fmt_signed ? "Signed" : "Unsigned", + dsp->fmt_bits, + dsp->dma_auto ? "Auto" : "Single", + dsp->block_size, + dsp->freq, + dsp->time_const, + dsp->speaker); +} +#endif + +static void speaker (SB16State *s, int on) +{ + s->speaker = on; + /* AUD_enable (s->voice, on); */ +} + +static void control (SB16State *s, int hold) +{ + int dma = s->use_hdma ? s->hdma : s->dma; + s->dma_running = hold; + + ldebug ("hold %d high %d dma %d\n", hold, s->use_hdma, dma); + + if (hold) { + DMA_hold_DREQ (dma); + AUD_set_active_out (s->voice, 1); + } + else { + DMA_release_DREQ (dma); + AUD_set_active_out (s->voice, 0); + } +} + +static void aux_timer (void *opaque) +{ + SB16State *s = opaque; + s->can_write = 1; + pic_set_irq (s->irq, 1); +} + +#define DMA8_AUTO 1 +#define DMA8_HIGH 2 + +static void dma_cmd8 (SB16State *s, int mask, int dma_len) +{ + s->fmt = AUD_FMT_U8; + s->use_hdma = 0; + s->fmt_bits = 8; + s->fmt_signed = 0; + s->fmt_stereo = (s->mixer_regs[0x0e] & 2) != 0; + if (-1 == s->time_const) { + s->freq = 11025; + } + else { + int tmp = (256 - s->time_const); + s->freq = (1000000 + (tmp / 2)) / tmp; + } + + if (dma_len != -1) { + s->block_size = dma_len << s->fmt_stereo; + } + else { + /* This is apparently the only way to make both Act1/PL + and SecondReality/FC work + + Act1 sets block size via command 0x48 and it's an odd number + SR does the same with even number + Both use stereo, and Creatives own documentation states that + 0x48 sets block size in bytes less one.. go figure */ + s->block_size &= ~s->fmt_stereo; + } + + s->freq >>= s->fmt_stereo; + s->left_till_irq = s->block_size; + s->bytes_per_second = (s->freq << s->fmt_stereo); + /* s->highspeed = (mask & DMA8_HIGH) != 0; */ + s->dma_auto = (mask & DMA8_AUTO) != 0; + s->align = (1 << s->fmt_stereo) - 1; + + if (s->block_size & s->align) { + dolog ("warning: misaligned block size %d, alignment %d\n", + s->block_size, s->align + 1); + } + + ldebug ("freq %d, stereo %d, sign %d, bits %d, " + "dma %d, auto %d, fifo %d, high %d\n", + s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits, + s->block_size, s->dma_auto, s->fifo, s->highspeed); + + if (s->freq) { + audsettings_t as; + + s->audio_free = 0; + + as.freq = s->freq; + as.nchannels = 1 << s->fmt_stereo; + as.fmt = s->fmt; + + s->voice = AUD_open_out ( + &s->card, + s->voice, + "sb16", + s, + SB_audio_callback, + &as, + 0 /* little endian */ + ); + } + + control (s, 1); + speaker (s, 1); +} + +static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len) +{ + s->use_hdma = cmd < 0xc0; + s->fifo = (cmd >> 1) & 1; + s->dma_auto = (cmd >> 2) & 1; + s->fmt_signed = (d0 >> 4) & 1; + s->fmt_stereo = (d0 >> 5) & 1; + + switch (cmd >> 4) { + case 11: + s->fmt_bits = 16; + break; + + case 12: + s->fmt_bits = 8; + break; + } + + if (-1 != s->time_const) { +#if 1 + int tmp = 256 - s->time_const; + s->freq = (1000000 + (tmp / 2)) / tmp; +#else + /* s->freq = 1000000 / ((255 - s->time_const) << s->fmt_stereo); */ + s->freq = 1000000 / ((255 - s->time_const)); +#endif + s->time_const = -1; + } + + s->block_size = dma_len + 1; + s->block_size <<= (s->fmt_bits == 16); + if (!s->dma_auto) { + /* It is clear that for DOOM and auto-init this value + shouldn't take stereo into account, while Miles Sound Systems + setsound.exe with single transfer mode wouldn't work without it + wonders of SB16 yet again */ + s->block_size <<= s->fmt_stereo; + } + + ldebug ("freq %d, stereo %d, sign %d, bits %d, " + "dma %d, auto %d, fifo %d, high %d\n", + s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits, + s->block_size, s->dma_auto, s->fifo, s->highspeed); + + if (16 == s->fmt_bits) { + if (s->fmt_signed) { + s->fmt = AUD_FMT_S16; + } + else { + s->fmt = AUD_FMT_U16; + } + } + else { + if (s->fmt_signed) { + s->fmt = AUD_FMT_S8; + } + else { + s->fmt = AUD_FMT_U8; + } + } + + s->left_till_irq = s->block_size; + + s->bytes_per_second = (s->freq << s->fmt_stereo) << (s->fmt_bits == 16); + s->highspeed = 0; + s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1; + if (s->block_size & s->align) { + dolog ("warning: misaligned block size %d, alignment %d\n", + s->block_size, s->align + 1); + } + + if (s->freq) { + audsettings_t as; + + s->audio_free = 0; + + as.freq = s->freq; + as.nchannels = 1 << s->fmt_stereo; + as.fmt = s->fmt; + + s->voice = AUD_open_out ( + &s->card, + s->voice, + "sb16", + s, + SB_audio_callback, + &as, + 0 /* little endian */ + ); + } + + control (s, 1); + speaker (s, 1); +} + +static inline void dsp_out_data (SB16State *s, uint8_t val) +{ + ldebug ("outdata %#x\n", val); + if ((size_t) s->out_data_len < sizeof (s->out_data)) { + s->out_data[s->out_data_len++] = val; + } +} + +static inline uint8_t dsp_get_data (SB16State *s) +{ + if (s->in_index) { + return s->in2_data[--s->in_index]; + } + else { + dolog ("buffer underflow\n"); + return 0; + } +} + +static void command (SB16State *s, uint8_t cmd) +{ + ldebug ("command %#x\n", cmd); + + if (cmd > 0xaf && cmd < 0xd0) { + if (cmd & 8) { + dolog ("ADC not yet supported (command %#x)\n", cmd); + } + + switch (cmd >> 4) { + case 11: + case 12: + break; + default: + dolog ("%#x wrong bits\n", cmd); + } + s->needed_bytes = 3; + } + else { + s->needed_bytes = 0; + + switch (cmd) { + case 0x03: + dsp_out_data (s, 0x10); /* s->csp_param); */ + goto warn; + + case 0x04: + s->needed_bytes = 1; + goto warn; + + case 0x05: + s->needed_bytes = 2; + goto warn; + + case 0x08: + /* __asm__ ("int3"); */ + goto warn; + + case 0x0e: + s->needed_bytes = 2; + goto warn; + + case 0x09: + dsp_out_data (s, 0xf8); + goto warn; + + case 0x0f: + s->needed_bytes = 1; + goto warn; + + case 0x10: + s->needed_bytes = 1; + goto warn; + + case 0x14: + s->needed_bytes = 2; + s->block_size = 0; + break; + + case 0x1c: /* Auto-Initialize DMA DAC, 8-bit */ + control (s, 1); + break; + + case 0x20: /* Direct ADC, Juice/PL */ + dsp_out_data (s, 0xff); + goto warn; + + case 0x35: + dolog ("0x35 - MIDI command not implemented\n"); + break; + + case 0x40: + s->freq = -1; + s->time_const = -1; + s->needed_bytes = 1; + break; + + case 0x41: + s->freq = -1; + s->time_const = -1; + s->needed_bytes = 2; + break; + + case 0x42: + s->freq = -1; + s->time_const = -1; + s->needed_bytes = 2; + goto warn; + + case 0x45: + dsp_out_data (s, 0xaa); + goto warn; + + case 0x47: /* Continue Auto-Initialize DMA 16bit */ + break; + + case 0x48: + s->needed_bytes = 2; + break; + + case 0x74: + s->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */ + dolog ("0x75 - DMA DAC, 4-bit ADPCM not implemented\n"); + break; + + case 0x75: /* DMA DAC, 4-bit ADPCM Reference */ + s->needed_bytes = 2; + dolog ("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n"); + break; + + case 0x76: /* DMA DAC, 2.6-bit ADPCM */ + s->needed_bytes = 2; + dolog ("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n"); + break; + + case 0x77: /* DMA DAC, 2.6-bit ADPCM Reference */ + s->needed_bytes = 2; + dolog ("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n"); + break; + + case 0x7d: + dolog ("0x7d - Autio-Initialize DMA DAC, 4-bit ADPCM Reference\n"); + dolog ("not implemented\n"); + break; + + case 0x7f: + dolog ( + "0x7d - Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference\n" + ); + dolog ("not implemented\n"); + break; + + case 0x80: + s->needed_bytes = 2; + break; + + case 0x90: + case 0x91: + dma_cmd8 (s, ((cmd & 1) == 0) | DMA8_HIGH, -1); + break; + + case 0xd0: /* halt DMA operation. 8bit */ + control (s, 0); + break; + + case 0xd1: /* speaker on */ + speaker (s, 1); + break; + + case 0xd3: /* speaker off */ + speaker (s, 0); + break; + + case 0xd4: /* continue DMA operation. 8bit */ + control (s, 1); + break; + + case 0xd5: /* halt DMA operation. 16bit */ + control (s, 0); + break; + + case 0xd6: /* continue DMA operation. 16bit */ + control (s, 1); + break; + + case 0xd9: /* exit auto-init DMA after this block. 16bit */ + s->dma_auto = 0; + break; + + case 0xda: /* exit auto-init DMA after this block. 8bit */ + s->dma_auto = 0; + break; + + case 0xe0: /* DSP identification */ + s->needed_bytes = 1; + break; + + case 0xe1: + dsp_out_data (s, s->ver & 0xff); + dsp_out_data (s, s->ver >> 8); + break; + + case 0xe2: + s->needed_bytes = 1; + goto warn; + + case 0xe3: + { + int i; + for (i = sizeof (e3) - 1; i >= 0; --i) + dsp_out_data (s, e3[i]); + } + break; + + case 0xe4: /* write test reg */ + s->needed_bytes = 1; + break; + + case 0xe7: + dolog ("Attempt to probe for ESS (0xe7)?\n"); + break; + + case 0xe8: /* read test reg */ + dsp_out_data (s, s->test_reg); + break; + + case 0xf2: + case 0xf3: + dsp_out_data (s, 0xaa); + s->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2; + pic_set_irq (s->irq, 1); + break; + + case 0xf9: + s->needed_bytes = 1; + goto warn; + + case 0xfa: + dsp_out_data (s, 0); + goto warn; + + case 0xfc: /* FIXME */ + dsp_out_data (s, 0); + goto warn; + + default: + dolog ("Unrecognized command %#x\n", cmd); + break; + } + } + + if (!s->needed_bytes) { + ldebug ("\n"); + } + + exit: + if (!s->needed_bytes) { + s->cmd = -1; + } + else { + s->cmd = cmd; + } + return; + + warn: + dolog ("warning: command %#x,%d is not truly understood yet\n", + cmd, s->needed_bytes); + goto exit; + +} + +static uint16_t dsp_get_lohi (SB16State *s) +{ + uint8_t hi = dsp_get_data (s); + uint8_t lo = dsp_get_data (s); + return (hi << 8) | lo; +} + +static uint16_t dsp_get_hilo (SB16State *s) +{ + uint8_t lo = dsp_get_data (s); + uint8_t hi = dsp_get_data (s); + return (hi << 8) | lo; +} + +static void complete (SB16State *s) +{ + int d0, d1, d2; + ldebug ("complete command %#x, in_index %d, needed_bytes %d\n", + s->cmd, s->in_index, s->needed_bytes); + + if (s->cmd > 0xaf && s->cmd < 0xd0) { + d2 = dsp_get_data (s); + d1 = dsp_get_data (s); + d0 = dsp_get_data (s); + + if (s->cmd & 8) { + dolog ("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", + s->cmd, d0, d1, d2); + } + else { + ldebug ("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", + s->cmd, d0, d1, d2); + dma_cmd (s, s->cmd, d0, d1 + (d2 << 8)); + } + } + else { + switch (s->cmd) { + case 0x04: + s->csp_mode = dsp_get_data (s); + s->csp_reg83r = 0; + s->csp_reg83w = 0; + ldebug ("CSP command 0x04: mode=%#x\n", s->csp_mode); + break; + + case 0x05: + s->csp_param = dsp_get_data (s); + s->csp_value = dsp_get_data (s); + ldebug ("CSP command 0x05: param=%#x value=%#x\n", + s->csp_param, + s->csp_value); + break; + + case 0x0e: + d0 = dsp_get_data (s); + d1 = dsp_get_data (s); + ldebug ("write CSP register %d <- %#x\n", d1, d0); + if (d1 == 0x83) { + ldebug ("0x83[%d] <- %#x\n", s->csp_reg83r, d0); + s->csp_reg83[s->csp_reg83r % 4] = d0; + s->csp_reg83r += 1; + } + else { + s->csp_regs[d1] = d0; + } + break; + + case 0x0f: + d0 = dsp_get_data (s); + ldebug ("read CSP register %#x -> %#x, mode=%#x\n", + d0, s->csp_regs[d0], s->csp_mode); + if (d0 == 0x83) { + ldebug ("0x83[%d] -> %#x\n", + s->csp_reg83w, + s->csp_reg83[s->csp_reg83w % 4]); + dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]); + s->csp_reg83w += 1; + } + else { + dsp_out_data (s, s->csp_regs[d0]); + } + break; + + case 0x10: + d0 = dsp_get_data (s); + dolog ("cmd 0x10 d0=%#x\n", d0); + break; + + case 0x14: + dma_cmd8 (s, 0, dsp_get_lohi (s) + 1); + break; + + case 0x40: + s->time_const = dsp_get_data (s); + ldebug ("set time const %d\n", s->time_const); + break; + + case 0x42: /* FT2 sets output freq with this, go figure */ +#if 0 + dolog ("cmd 0x42 might not do what it think it should\n"); +#endif + case 0x41: + s->freq = dsp_get_hilo (s); + ldebug ("set freq %d\n", s->freq); + break; + + case 0x48: + s->block_size = dsp_get_lohi (s) + 1; + ldebug ("set dma block len %d\n", s->block_size); + break; + + case 0x74: + case 0x75: + case 0x76: + case 0x77: + /* ADPCM stuff, ignore */ + break; + + case 0x80: + { + int freq, samples, bytes; + int64_t ticks; + + freq = s->freq > 0 ? s->freq : 11025; + samples = dsp_get_lohi (s) + 1; + bytes = samples << s->fmt_stereo << (s->fmt_bits == 16); + ticks = (bytes * ticks_per_sec) / freq; + if (ticks < ticks_per_sec / 1024) { + pic_set_irq (s->irq, 1); + } + else { + if (s->aux_ts) { + qemu_mod_timer ( + s->aux_ts, + qemu_get_clock (vm_clock) + ticks + ); + } + } + ldebug ("mix silence %d %d %lld\n", samples, bytes, ticks); + } + break; + + case 0xe0: + d0 = dsp_get_data (s); + s->out_data_len = 0; + ldebug ("E0 data = %#x\n", d0); + dsp_out_data (s, ~d0); + break; + + case 0xe2: + d0 = dsp_get_data (s); + ldebug ("E2 = %#x\n", d0); + break; + + case 0xe4: + s->test_reg = dsp_get_data (s); + break; + + case 0xf9: + d0 = dsp_get_data (s); + ldebug ("command 0xf9 with %#x\n", d0); + switch (d0) { + case 0x0e: + dsp_out_data (s, 0xff); + break; + + case 0x0f: + dsp_out_data (s, 0x07); + break; + + case 0x37: + dsp_out_data (s, 0x38); + break; + + default: + dsp_out_data (s, 0x00); + break; + } + break; + + default: + dolog ("complete: unrecognized command %#x\n", s->cmd); + return; + } + } + + ldebug ("\n"); + s->cmd = -1; + return; +} + +static void reset (SB16State *s) +{ + pic_set_irq (s->irq, 0); + if (s->dma_auto) { + pic_set_irq (s->irq, 1); + pic_set_irq (s->irq, 0); + } + + s->mixer_regs[0x82] = 0; + s->dma_auto = 0; + s->in_index = 0; + s->out_data_len = 0; + s->left_till_irq = 0; + s->needed_bytes = 0; + s->block_size = -1; + s->nzero = 0; + s->highspeed = 0; + s->v2x6 = 0; + s->cmd = -1; + + dsp_out_data(s, 0xaa); + speaker (s, 0); + control (s, 0); +} + +static IO_WRITE_PROTO (dsp_write) +{ + SB16State *s = opaque; + int iport; + + iport = nport - s->port; + + ldebug ("write %#x <- %#x\n", nport, val); + switch (iport) { + case 0x06: + switch (val) { + case 0x00: + if (s->v2x6 == 1) { + if (0 && s->highspeed) { + s->highspeed = 0; + pic_set_irq (s->irq, 0); + control (s, 0); + } + else { + reset (s); + } + } + s->v2x6 = 0; + break; + + case 0x01: + case 0x03: /* FreeBSD kludge */ + s->v2x6 = 1; + break; + + case 0xc6: + s->v2x6 = 0; /* Prince of Persia, csp.sys, diagnose.exe */ + break; + + case 0xb8: /* Panic */ + reset (s); + break; + + case 0x39: + dsp_out_data (s, 0x38); + reset (s); + s->v2x6 = 0x39; + break; + + default: + s->v2x6 = val; + break; + } + break; + + case 0x0c: /* write data or command | write status */ +/* if (s->highspeed) */ +/* break; */ + + if (0 == s->needed_bytes) { + command (s, val); +#if 0 + if (0 == s->needed_bytes) { + log_dsp (s); + } +#endif + } + else { + if (s->in_index == sizeof (s->in2_data)) { + dolog ("in data overrun\n"); + } + else { + s->in2_data[s->in_index++] = val; + if (s->in_index == s->needed_bytes) { + s->needed_bytes = 0; + complete (s); +#if 0 + log_dsp (s); +#endif + } + } + } + break; + + default: + ldebug ("(nport=%#x, val=%#x)\n", nport, val); + break; + } +} + +static IO_READ_PROTO (dsp_read) +{ + SB16State *s = opaque; + int iport, retval, ack = 0; + + iport = nport - s->port; + + switch (iport) { + case 0x06: /* reset */ + retval = 0xff; + break; + + case 0x0a: /* read data */ + if (s->out_data_len) { + retval = s->out_data[--s->out_data_len]; + s->last_read_byte = retval; + } + else { + if (s->cmd != -1) { + dolog ("empty output buffer for command %#x\n", + s->cmd); + } + retval = s->last_read_byte; + /* goto error; */ + } + break; + + case 0x0c: /* 0 can write */ + retval = s->can_write ? 0 : 0x80; + break; + + case 0x0d: /* timer interrupt clear */ + /* dolog ("timer interrupt clear\n"); */ + retval = 0; + break; + + case 0x0e: /* data available status | irq 8 ack */ + retval = (!s->out_data_len || s->highspeed) ? 0 : 0x80; + if (s->mixer_regs[0x82] & 1) { + ack = 1; + s->mixer_regs[0x82] &= 1; + pic_set_irq (s->irq, 0); + } + break; + + case 0x0f: /* irq 16 ack */ + retval = 0xff; + if (s->mixer_regs[0x82] & 2) { + ack = 1; + s->mixer_regs[0x82] &= 2; + pic_set_irq (s->irq, 0); + } + break; + + default: + goto error; + } + + if (!ack) { + ldebug ("read %#x -> %#x\n", nport, retval); + } + + return retval; + + error: + dolog ("warning: dsp_read %#x error\n", nport); + return 0xff; +} + +static void reset_mixer (SB16State *s) +{ + int i; + + memset (s->mixer_regs, 0xff, 0x7f); + memset (s->mixer_regs + 0x83, 0xff, sizeof (s->mixer_regs) - 0x83); + + s->mixer_regs[0x02] = 4; /* master volume 3bits */ + s->mixer_regs[0x06] = 4; /* MIDI volume 3bits */ + s->mixer_regs[0x08] = 0; /* CD volume 3bits */ + s->mixer_regs[0x0a] = 0; /* voice volume 2bits */ + + /* d5=input filt, d3=lowpass filt, d1,d2=input source */ + s->mixer_regs[0x0c] = 0; + + /* d5=output filt, d1=stereo switch */ + s->mixer_regs[0x0e] = 0; + + /* voice volume L d5,d7, R d1,d3 */ + s->mixer_regs[0x04] = (4 << 5) | (4 << 1); + /* master ... */ + s->mixer_regs[0x22] = (4 << 5) | (4 << 1); + /* MIDI ... */ + s->mixer_regs[0x26] = (4 << 5) | (4 << 1); + + for (i = 0x30; i < 0x48; i++) { + s->mixer_regs[i] = 0x20; + } +} + +static IO_WRITE_PROTO(mixer_write_indexb) +{ + SB16State *s = opaque; + (void) nport; + s->mixer_nreg = val; +} + +static IO_WRITE_PROTO(mixer_write_datab) +{ + SB16State *s = opaque; + + (void) nport; + ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val); + + switch (s->mixer_nreg) { + case 0x00: + reset_mixer (s); + break; + + case 0x80: + { + int irq = irq_of_magic (val); + ldebug ("setting irq to %d (val=%#x)\n", irq, val); + if (irq > 0) { + s->irq = irq; + } + } + break; + + case 0x81: + { + int dma, hdma; + + dma = lsbindex (val & 0xf); + hdma = lsbindex (val & 0xf0); + if (dma != s->dma || hdma != s->hdma) { + dolog ( + "attempt to change DMA " + "8bit %d(%d), 16bit %d(%d) (val=%#x)\n", + dma, s->dma, hdma, s->hdma, val); + } +#if 0 + s->dma = dma; + s->hdma = hdma; +#endif + } + break; + + case 0x82: + dolog ("attempt to write into IRQ status register (val=%#x)\n", + val); + return; + + default: + if (s->mixer_nreg >= 0x80) { + ldebug ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val); + } + break; + } + + s->mixer_regs[s->mixer_nreg] = val; +} + +static IO_WRITE_PROTO(mixer_write_indexw) +{ + mixer_write_indexb (opaque, nport, val & 0xff); + mixer_write_datab (opaque, nport, (val >> 8) & 0xff); +} + +static IO_READ_PROTO(mixer_read) +{ + SB16State *s = opaque; + + (void) nport; +#ifndef DEBUG_SB16_MOST + if (s->mixer_nreg != 0x82) { + ldebug ("mixer_read[%#x] -> %#x\n", + s->mixer_nreg, s->mixer_regs[s->mixer_nreg]); + } +#else + ldebug ("mixer_read[%#x] -> %#x\n", + s->mixer_nreg, s->mixer_regs[s->mixer_nreg]); +#endif + return s->mixer_regs[s->mixer_nreg]; +} + +static int write_audio (SB16State *s, int nchan, int dma_pos, + int dma_len, int len) +{ + int temp, net; + uint8_t tmpbuf[4096]; + + temp = len; + net = 0; + + while (temp) { + int left = dma_len - dma_pos; + int copied; + size_t to_copy; + + to_copy = audio_MIN (temp, left); + if (to_copy > sizeof (tmpbuf)) { + to_copy = sizeof (tmpbuf); + } + + copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy); + copied = AUD_write (s->voice, tmpbuf, copied); + + temp -= copied; + dma_pos = (dma_pos + copied) % dma_len; + net += copied; + + if (!copied) { + break; + } + } + + return net; +} + +static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) +{ + SB16State *s = opaque; + int till, copy, written, free; + + if (s->left_till_irq < 0) { + s->left_till_irq = s->block_size; + } + + if (s->voice) { + free = s->audio_free & ~s->align; + if ((free <= 0) || !dma_len) { + return dma_pos; + } + } + else { + free = dma_len; + } + + copy = free; + till = s->left_till_irq; + +#ifdef DEBUG_SB16_MOST + dolog ("pos:%06d %d till:%d len:%d\n", + dma_pos, free, till, dma_len); +#endif + + if (till <= copy) { + if (0 == s->dma_auto) { + copy = till; + } + } + + written = write_audio (s, nchan, dma_pos, dma_len, copy); + dma_pos = (dma_pos + written) % dma_len; + s->left_till_irq -= written; + + if (s->left_till_irq <= 0) { + s->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1; + pic_set_irq (s->irq, 1); + if (0 == s->dma_auto) { + control (s, 0); + speaker (s, 0); + } + } + +#ifdef DEBUG_SB16_MOST + ldebug ("pos %5d free %5d size %5d till % 5d copy %5d written %5d size %5d\n", + dma_pos, free, dma_len, s->left_till_irq, copy, written, + s->block_size); +#endif + + while (s->left_till_irq <= 0) { + s->left_till_irq = s->block_size + s->left_till_irq; + } + + return dma_pos; +} + +static void SB_audio_callback (void *opaque, int free) +{ + SB16State *s = opaque; + s->audio_free = free; +} + +static void SB_save (QEMUFile *f, void *opaque) +{ + SB16State *s = opaque; + + qemu_put_be32s (f, &s->irq); + qemu_put_be32s (f, &s->dma); + qemu_put_be32s (f, &s->hdma); + qemu_put_be32s (f, &s->port); + qemu_put_be32s (f, &s->ver); + qemu_put_be32s (f, &s->in_index); + qemu_put_be32s (f, &s->out_data_len); + qemu_put_be32s (f, &s->fmt_stereo); + qemu_put_be32s (f, &s->fmt_signed); + qemu_put_be32s (f, &s->fmt_bits); + qemu_put_be32s (f, &s->fmt); + qemu_put_be32s (f, &s->dma_auto); + qemu_put_be32s (f, &s->block_size); + qemu_put_be32s (f, &s->fifo); + qemu_put_be32s (f, &s->freq); + qemu_put_be32s (f, &s->time_const); + qemu_put_be32s (f, &s->speaker); + qemu_put_be32s (f, &s->needed_bytes); + qemu_put_be32s (f, &s->cmd); + qemu_put_be32s (f, &s->use_hdma); + qemu_put_be32s (f, &s->highspeed); + qemu_put_be32s (f, &s->can_write); + qemu_put_be32s (f, &s->v2x6); + + qemu_put_8s (f, &s->csp_param); + qemu_put_8s (f, &s->csp_value); + qemu_put_8s (f, &s->csp_mode); + qemu_put_8s (f, &s->csp_param); + qemu_put_buffer (f, s->csp_regs, 256); + qemu_put_8s (f, &s->csp_index); + qemu_put_buffer (f, s->csp_reg83, 4); + qemu_put_be32s (f, &s->csp_reg83r); + qemu_put_be32s (f, &s->csp_reg83w); + + qemu_put_buffer (f, s->in2_data, sizeof (s->in2_data)); + qemu_put_buffer (f, s->out_data, sizeof (s->out_data)); + qemu_put_8s (f, &s->test_reg); + qemu_put_8s (f, &s->last_read_byte); + + qemu_put_be32s (f, &s->nzero); + qemu_put_be32s (f, &s->left_till_irq); + qemu_put_be32s (f, &s->dma_running); + qemu_put_be32s (f, &s->bytes_per_second); + qemu_put_be32s (f, &s->align); + + qemu_put_be32s (f, &s->mixer_nreg); + qemu_put_buffer (f, s->mixer_regs, 256); +} + +static int SB_load (QEMUFile *f, void *opaque, int version_id) +{ + SB16State *s = opaque; + + if (version_id != 1) { + return -EINVAL; + } + + qemu_get_be32s (f, &s->irq); + qemu_get_be32s (f, &s->dma); + qemu_get_be32s (f, &s->hdma); + qemu_get_be32s (f, &s->port); + qemu_get_be32s (f, &s->ver); + qemu_get_be32s (f, &s->in_index); + qemu_get_be32s (f, &s->out_data_len); + qemu_get_be32s (f, &s->fmt_stereo); + qemu_get_be32s (f, &s->fmt_signed); + qemu_get_be32s (f, &s->fmt_bits); + qemu_get_be32s (f, &s->fmt); + qemu_get_be32s (f, &s->dma_auto); + qemu_get_be32s (f, &s->block_size); + qemu_get_be32s (f, &s->fifo); + qemu_get_be32s (f, &s->freq); + qemu_get_be32s (f, &s->time_const); + qemu_get_be32s (f, &s->speaker); + qemu_get_be32s (f, &s->needed_bytes); + qemu_get_be32s (f, &s->cmd); + qemu_get_be32s (f, &s->use_hdma); + qemu_get_be32s (f, &s->highspeed); + qemu_get_be32s (f, &s->can_write); + qemu_get_be32s (f, &s->v2x6); + + qemu_get_8s (f, &s->csp_param); + qemu_get_8s (f, &s->csp_value); + qemu_get_8s (f, &s->csp_mode); + qemu_get_8s (f, &s->csp_param); + qemu_get_buffer (f, s->csp_regs, 256); + qemu_get_8s (f, &s->csp_index); + qemu_get_buffer (f, s->csp_reg83, 4); + qemu_get_be32s (f, &s->csp_reg83r); + qemu_get_be32s (f, &s->csp_reg83w); + + qemu_get_buffer (f, s->in2_data, sizeof (s->in2_data)); + qemu_get_buffer (f, s->out_data, sizeof (s->out_data)); + qemu_get_8s (f, &s->test_reg); + qemu_get_8s (f, &s->last_read_byte); + + qemu_get_be32s (f, &s->nzero); + qemu_get_be32s (f, &s->left_till_irq); + qemu_get_be32s (f, &s->dma_running); + qemu_get_be32s (f, &s->bytes_per_second); + qemu_get_be32s (f, &s->align); + + qemu_get_be32s (f, &s->mixer_nreg); + qemu_get_buffer (f, s->mixer_regs, 256); + + if (s->voice) { + AUD_close_out (&s->card, s->voice); + s->voice = NULL; + } + + if (s->dma_running) { + if (s->freq) { + audsettings_t as; + + s->audio_free = 0; + + as.freq = s->freq; + as.nchannels = 1 << s->fmt_stereo; + as.fmt = s->fmt; + + s->voice = AUD_open_out ( + &s->card, + s->voice, + "sb16", + s, + SB_audio_callback, + &as, + 0 /* little endian */ + ); + } + + control (s, 1); + speaker (s, s->speaker); + } + return 0; +} + +int SB16_init (AudioState *audio) +{ + SB16State *s; + int i; + static const uint8_t dsp_write_ports[] = {0x6, 0xc}; + static const uint8_t dsp_read_ports[] = {0x6, 0xa, 0xc, 0xd, 0xe, 0xf}; + + if (!audio) { + dolog ("No audio state\n"); + return -1; + } + + s = qemu_mallocz (sizeof (*s)); + if (!s) { + dolog ("Could not allocate memory for SB16 (%zu bytes)\n", + sizeof (*s)); + return -1; + } + + s->cmd = -1; + s->irq = conf.irq; + s->dma = conf.dma; + s->hdma = conf.hdma; + s->port = conf.port; + s->ver = conf.ver_lo | (conf.ver_hi << 8); + + s->mixer_regs[0x80] = magic_of_irq (s->irq); + s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma); + s->mixer_regs[0x82] = 2 << 5; + + s->csp_regs[5] = 1; + s->csp_regs[9] = 0xf8; + + reset_mixer (s); + s->aux_ts = qemu_new_timer (vm_clock, aux_timer, s); + if (!s->aux_ts) { + dolog ("warning: Could not create auxiliary timer\n"); + } + + for (i = 0; i < LENOFA (dsp_write_ports); i++) { + register_ioport_write (s->port + dsp_write_ports[i], 1, 1, dsp_write, s); + } + + for (i = 0; i < LENOFA (dsp_read_ports); i++) { + register_ioport_read (s->port + dsp_read_ports[i], 1, 1, dsp_read, s); + } + + register_ioport_write (s->port + 0x4, 1, 1, mixer_write_indexb, s); + register_ioport_write (s->port + 0x4, 1, 2, mixer_write_indexw, s); + register_ioport_read (s->port + 0x5, 1, 1, mixer_read, s); + register_ioport_write (s->port + 0x5, 1, 1, mixer_write_datab, s); + + DMA_register_channel (s->hdma, SB_read_DMA, s); + DMA_register_channel (s->dma, SB_read_DMA, s); + s->can_write = 1; + + register_savevm ("sb16", 0, 1, SB_save, SB_load, s); + AUD_register_card (audio, "sb16", &s->card); + return 0; +} diff --git a/tools/ioemu/hw/serial.c b/tools/ioemu/hw/serial.c new file mode 100644 index 0000000000..f36beb209f --- /dev/null +++ b/tools/ioemu/hw/serial.c @@ -0,0 +1,456 @@ +/* + * QEMU 16450 UART emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG_SERIAL + +#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ + +#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ +#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ +#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ +#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ + +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ +#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ + +#define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ +#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ +#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ + +/* + * These are the definitions for the Modem Control Register + */ +#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ +#define UART_MCR_OUT2 0x08 /* Out2 complement */ +#define UART_MCR_OUT1 0x04 /* Out1 complement */ +#define UART_MCR_RTS 0x02 /* RTS complement */ +#define UART_MCR_DTR 0x01 /* DTR complement */ + +/* + * These are the definitions for the Modem Status Register + */ +#define UART_MSR_DCD 0x80 /* Data Carrier Detect */ +#define UART_MSR_RI 0x40 /* Ring Indicator */ +#define UART_MSR_DSR 0x20 /* Data Set Ready */ +#define UART_MSR_CTS 0x10 /* Clear to Send */ +#define UART_MSR_DDCD 0x08 /* Delta DCD */ +#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */ +#define UART_MSR_DDSR 0x02 /* Delta DSR */ +#define UART_MSR_DCTS 0x01 /* Delta CTS */ +#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */ + +#define UART_LSR_TEMT 0x40 /* Transmitter empty */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x10 /* Break interrupt indicator */ +#define UART_LSR_FE 0x08 /* Frame error indicator */ +#define UART_LSR_PE 0x04 /* Parity error indicator */ +#define UART_LSR_OE 0x02 /* Overrun error indicator */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ + +struct SerialState { + uint8_t divider; + uint8_t rbr; /* receive register */ + uint8_t ier; + uint8_t iir; /* read only */ + uint8_t lcr; + uint8_t mcr; + uint8_t lsr; /* read only */ + uint8_t msr; /* read only */ + uint8_t scr; + /* NOTE: this hidden state is necessary for tx irq generation as + it can be reset while reading iir */ + int thr_ipending; + SetIRQFunc *set_irq; + void *irq_opaque; + int irq; + CharDriverState *chr; + int last_break_enable; + target_ulong base; + int it_shift; +}; + +static void serial_update_irq(SerialState *s) +{ + if ((s->lsr & UART_LSR_DR) && (s->ier & UART_IER_RDI)) { + s->iir = UART_IIR_RDI; + } else if (s->thr_ipending && (s->ier & UART_IER_THRI)) { + s->iir = UART_IIR_THRI; + } else { + s->iir = UART_IIR_NO_INT; + } + if (s->iir != UART_IIR_NO_INT) { + s->set_irq(s->irq_opaque, s->irq, 1); + } else { + s->set_irq(s->irq_opaque, s->irq, 0); + } +} + +static void serial_update_parameters(SerialState *s) +{ + int speed, parity, data_bits, stop_bits; + QEMUSerialSetParams ssp; + + if (s->lcr & 0x08) { + if (s->lcr & 0x10) + parity = 'E'; + else + parity = 'O'; + } else { + parity = 'N'; + } + if (s->lcr & 0x04) + stop_bits = 2; + else + stop_bits = 1; + data_bits = (s->lcr & 0x03) + 5; + if (s->divider == 0) + return; + speed = 115200 / s->divider; + ssp.speed = speed; + ssp.parity = parity; + ssp.data_bits = data_bits; + ssp.stop_bits = stop_bits; + qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); +#if 0 + printf("speed=%d parity=%c data=%d stop=%d\n", + speed, parity, data_bits, stop_bits); +#endif +} + +static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + SerialState *s = opaque; + unsigned char ch; + + addr &= 7; +#ifdef DEBUG_SERIAL + printf("serial: write addr=0x%02x val=0x%02x\n", addr, val); +#endif + switch(addr) { + default: + case 0: + if (s->lcr & UART_LCR_DLAB) { + s->divider = (s->divider & 0xff00) | val; + serial_update_parameters(s); + } else { + s->thr_ipending = 0; + s->lsr &= ~UART_LSR_THRE; + serial_update_irq(s); + ch = val; + qemu_chr_write(s->chr, &ch, 1); + s->thr_ipending = 1; + s->lsr |= UART_LSR_THRE; + s->lsr |= UART_LSR_TEMT; + serial_update_irq(s); + } + break; + case 1: + if (s->lcr & UART_LCR_DLAB) { + s->divider = (s->divider & 0x00ff) | (val << 8); + serial_update_parameters(s); + } else { + s->ier = val & 0x0f; + if (s->lsr & UART_LSR_THRE) { + s->thr_ipending = 1; + } + serial_update_irq(s); + } + break; + case 2: + break; + case 3: + { + int break_enable; + s->lcr = val; + serial_update_parameters(s); + break_enable = (val >> 6) & 1; + if (break_enable != s->last_break_enable) { + s->last_break_enable = break_enable; + qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK, + &break_enable); + } + } + break; + case 4: + s->mcr = val & 0x1f; + break; + case 5: + break; + case 6: + break; + case 7: + s->scr = val; + break; + } +} + +static uint32_t serial_ioport_read(void *opaque, uint32_t addr) +{ + SerialState *s = opaque; + uint32_t ret; + + addr &= 7; + switch(addr) { + default: + case 0: + if (s->lcr & UART_LCR_DLAB) { + ret = s->divider & 0xff; + } else { + ret = s->rbr; + s->lsr &= ~(UART_LSR_DR | UART_LSR_BI); + serial_update_irq(s); + } + break; + case 1: + if (s->lcr & UART_LCR_DLAB) { + ret = (s->divider >> 8) & 0xff; + } else { + ret = s->ier; + } + break; + case 2: + ret = s->iir; + /* reset THR pending bit */ + if ((ret & 0x7) == UART_IIR_THRI) + s->thr_ipending = 0; + serial_update_irq(s); + break; + case 3: + ret = s->lcr; + break; + case 4: + ret = s->mcr; + break; + case 5: + ret = s->lsr; + break; + case 6: + if (s->mcr & UART_MCR_LOOP) { + /* in loopback, the modem output pins are connected to the + inputs */ + ret = (s->mcr & 0x0c) << 4; + ret |= (s->mcr & 0x02) << 3; + ret |= (s->mcr & 0x01) << 5; + } else { + ret = s->msr; + } + break; + case 7: + ret = s->scr; + break; + } +#ifdef DEBUG_SERIAL + printf("serial: read addr=0x%02x val=0x%02x\n", addr, ret); +#endif + return ret; +} + +static int serial_can_receive(SerialState *s) +{ + return !(s->lsr & UART_LSR_DR); +} + +static void serial_receive_byte(SerialState *s, int ch) +{ + s->rbr = ch; + s->lsr |= UART_LSR_DR; + serial_update_irq(s); +} + +static void serial_receive_break(SerialState *s) +{ + s->rbr = 0; + s->lsr |= UART_LSR_BI | UART_LSR_DR; + serial_update_irq(s); +} + +static int serial_can_receive1(void *opaque) +{ + SerialState *s = opaque; + return serial_can_receive(s); +} + +static void serial_receive1(void *opaque, const uint8_t *buf, int size) +{ + SerialState *s = opaque; + serial_receive_byte(s, buf[0]); +} + +static void serial_event(void *opaque, int event) +{ + SerialState *s = opaque; + if (event == CHR_EVENT_BREAK) + serial_receive_break(s); +} + +static void serial_save(QEMUFile *f, void *opaque) +{ + SerialState *s = opaque; + + qemu_put_8s(f,&s->divider); + qemu_put_8s(f,&s->rbr); + qemu_put_8s(f,&s->ier); + qemu_put_8s(f,&s->iir); + qemu_put_8s(f,&s->lcr); + qemu_put_8s(f,&s->mcr); + qemu_put_8s(f,&s->lsr); + qemu_put_8s(f,&s->msr); + qemu_put_8s(f,&s->scr); +} + +static int serial_load(QEMUFile *f, void *opaque, int version_id) +{ + SerialState *s = opaque; + + if(version_id != 1) + return -EINVAL; + + qemu_get_8s(f,&s->divider); + qemu_get_8s(f,&s->rbr); + qemu_get_8s(f,&s->ier); + qemu_get_8s(f,&s->iir); + qemu_get_8s(f,&s->lcr); + qemu_get_8s(f,&s->mcr); + qemu_get_8s(f,&s->lsr); + qemu_get_8s(f,&s->msr); + qemu_get_8s(f,&s->scr); + + return 0; +} + +/* If fd is zero, it means that the serial device uses the console */ +SerialState *serial_init(SetIRQFunc *set_irq, void *opaque, + int base, int irq, CharDriverState *chr) +{ + SerialState *s; + + s = qemu_mallocz(sizeof(SerialState)); + if (!s) + return NULL; + s->set_irq = set_irq; + s->irq_opaque = opaque; + s->irq = irq; + s->lsr = UART_LSR_TEMT | UART_LSR_THRE; + s->iir = UART_IIR_NO_INT; + s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS; + + register_savevm("serial", base, 1, serial_save, serial_load, s); + + register_ioport_write(base, 8, 1, serial_ioport_write, s); + register_ioport_read(base, 8, 1, serial_ioport_read, s); + s->chr = chr; + qemu_chr_add_read_handler(chr, serial_can_receive1, serial_receive1, s); + qemu_chr_add_event_handler(chr, serial_event); + return s; +} + +/* Memory mapped interface */ +static uint32_t serial_mm_readb (void *opaque, target_phys_addr_t addr) +{ + SerialState *s = opaque; + + return serial_ioport_read(s, (addr - s->base) >> s->it_shift) & 0xFF; +} + +static void serial_mm_writeb (void *opaque, + target_phys_addr_t addr, uint32_t value) +{ + SerialState *s = opaque; + + serial_ioport_write(s, (addr - s->base) >> s->it_shift, value & 0xFF); +} + +static uint32_t serial_mm_readw (void *opaque, target_phys_addr_t addr) +{ + SerialState *s = opaque; + + return serial_ioport_read(s, (addr - s->base) >> s->it_shift) & 0xFFFF; +} + +static void serial_mm_writew (void *opaque, + target_phys_addr_t addr, uint32_t value) +{ + SerialState *s = opaque; + + serial_ioport_write(s, (addr - s->base) >> s->it_shift, value & 0xFFFF); +} + +static uint32_t serial_mm_readl (void *opaque, target_phys_addr_t addr) +{ + SerialState *s = opaque; + + return serial_ioport_read(s, (addr - s->base) >> s->it_shift); +} + +static void serial_mm_writel (void *opaque, + target_phys_addr_t addr, uint32_t value) +{ + SerialState *s = opaque; + + serial_ioport_write(s, (addr - s->base) >> s->it_shift, value); +} + +static CPUReadMemoryFunc *serial_mm_read[] = { + &serial_mm_readb, + &serial_mm_readw, + &serial_mm_readl, +}; + +static CPUWriteMemoryFunc *serial_mm_write[] = { + &serial_mm_writeb, + &serial_mm_writew, + &serial_mm_writel, +}; + +SerialState *serial_mm_init (SetIRQFunc *set_irq, void *opaque, + target_ulong base, int it_shift, + int irq, CharDriverState *chr) +{ + SerialState *s; + int s_io_memory; + + s = qemu_mallocz(sizeof(SerialState)); + if (!s) + return NULL; + s->set_irq = set_irq; + s->irq_opaque = opaque; + s->irq = irq; + s->lsr = UART_LSR_TEMT | UART_LSR_THRE; + s->iir = UART_IIR_NO_INT; + s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS; + s->base = base; + s->it_shift = it_shift; + + register_savevm("serial", base, 1, serial_save, serial_load, s); + + s_io_memory = cpu_register_io_memory(0, serial_mm_read, + serial_mm_write, s); + cpu_register_physical_memory(base, 8 << it_shift, s_io_memory); + s->chr = chr; + qemu_chr_add_read_handler(chr, serial_can_receive1, serial_receive1, s); + qemu_chr_add_event_handler(chr, serial_event); + return s; +} diff --git a/tools/ioemu/hw/sh7750.c b/tools/ioemu/hw/sh7750.c new file mode 100644 index 0000000000..041e3eed1b --- /dev/null +++ b/tools/ioemu/hw/sh7750.c @@ -0,0 +1,836 @@ +/* + * SH7750 device + * + * Copyright (c) 2005 Samuel Tardieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include "vl.h" +#include "sh7750_regs.h" +#include "sh7750_regnames.h" + +typedef struct { + uint8_t data[16]; + uint8_t length; /* Number of characters in the FIFO */ + uint8_t write_idx; /* Index of first character to write */ + uint8_t read_idx; /* Index of first character to read */ +} fifo; + +#define NB_DEVICES 4 + +typedef struct SH7750State { + /* CPU */ + CPUSH4State *cpu; + /* Peripheral frequency in Hz */ + uint32_t periph_freq; + /* SDRAM controller */ + uint16_t rfcr; + /* First serial port */ + CharDriverState *serial1; + uint8_t scscr1; + uint8_t scsmr1; + uint8_t scbrr1; + uint8_t scssr1; + uint8_t scssr1_read; + uint8_t sctsr1; + uint8_t sctsr1_loaded; + uint8_t sctdr1; + uint8_t scrdr1; + /* Second serial port */ + CharDriverState *serial2; + uint16_t sclsr2; + uint16_t scscr2; + uint16_t scfcr2; + uint16_t scfsr2; + uint16_t scsmr2; + uint8_t scbrr2; + fifo serial2_receive_fifo; + fifo serial2_transmit_fifo; + /* Timers */ + uint8_t tstr; + /* Timer 0 */ + QEMUTimer *timer0; + uint16_t tcr0; + uint32_t tcor0; + uint32_t tcnt0; + /* IO ports */ + uint16_t gpioic; + uint32_t pctra; + uint32_t pctrb; + uint16_t portdira; /* Cached */ + uint16_t portpullupa; /* Cached */ + uint16_t portdirb; /* Cached */ + uint16_t portpullupb; /* Cached */ + uint16_t pdtra; + uint16_t pdtrb; + uint16_t periph_pdtra; /* Imposed by the peripherals */ + uint16_t periph_portdira; /* Direction seen from the peripherals */ + uint16_t periph_pdtrb; /* Imposed by the peripherals */ + uint16_t periph_portdirb; /* Direction seen from the peripherals */ + sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */ + /* Cache */ + uint32_t ccr; +} SH7750State; + +/********************************************************************** + Timers +**********************************************************************/ + +/* XXXXX At this time, timer0 works in underflow only mode, that is + the value of tcnt0 is read at alarm computation time and cannot + be read back by the guest OS */ + +static void start_timer0(SH7750State * s) +{ + uint64_t now, next, prescaler; + + if ((s->tcr0 & 6) == 6) { + fprintf(stderr, "rtc clock for timer 0 not supported\n"); + assert(0); + } + + if ((s->tcr0 & 7) == 5) { + fprintf(stderr, "timer 0 configuration not supported\n"); + assert(0); + } + + if ((s->tcr0 & 4) == 4) + prescaler = 1024; + else + prescaler = 4 << (s->tcr0 & 3); + + now = qemu_get_clock(vm_clock); + /* XXXXX */ + next = + now + muldiv64(prescaler * s->tcnt0, ticks_per_sec, + s->periph_freq); + if (next == now) + next = now + 1; + fprintf(stderr, "now=%016llx, next=%016llx\n", now, next); + fprintf(stderr, "timer will underflow in %f seconds\n", + (float) (next - now) / (float) ticks_per_sec); + + qemu_mod_timer(s->timer0, next); +} + +static void timer_start_changed(SH7750State * s) +{ + if (s->tstr & SH7750_TSTR_STR0) { + start_timer0(s); + } else { + fprintf(stderr, "timer 0 is stopped\n"); + qemu_del_timer(s->timer0); + } +} + +static void timer0_cb(void *opaque) +{ + SH7750State *s = opaque; + + s->tcnt0 = (uint32_t) 0; /* XXXXX */ + if (--s->tcnt0 == (uint32_t) - 1) { + fprintf(stderr, "timer 0 underflow\n"); + s->tcnt0 = s->tcor0; + s->tcr0 |= SH7750_TCR_UNF; + if (s->tcr0 & SH7750_TCR_UNIE) { + fprintf(stderr, + "interrupt generation for timer 0 not supported\n"); + assert(0); + } + } + start_timer0(s); +} + +static void init_timers(SH7750State * s) +{ + s->tcor0 = 0xffffffff; + s->tcnt0 = 0xffffffff; + s->timer0 = qemu_new_timer(vm_clock, &timer0_cb, s); +} + +/********************************************************************** + First serial port +**********************************************************************/ + +static int serial1_can_receive(void *opaque) +{ + SH7750State *s = opaque; + + return s->scscr1 & SH7750_SCSCR_RE; +} + +static void serial1_receive_char(SH7750State * s, uint8_t c) +{ + if (s->scssr1 & SH7750_SCSSR1_RDRF) { + s->scssr1 |= SH7750_SCSSR1_ORER; + return; + } + + s->scrdr1 = c; + s->scssr1 |= SH7750_SCSSR1_RDRF; +} + +static void serial1_receive(void *opaque, const uint8_t * buf, int size) +{ + SH7750State *s = opaque; + int i; + + for (i = 0; i < size; i++) { + serial1_receive_char(s, buf[i]); + } +} + +static void serial1_event(void *opaque, int event) +{ + assert(0); +} + +static void serial1_maybe_send(SH7750State * s) +{ + uint8_t c; + + if (s->scssr1 & SH7750_SCSSR1_TDRE) + return; + c = s->sctdr1; + s->scssr1 |= SH7750_SCSSR1_TDRE | SH7750_SCSSR1_TEND; + if (s->scscr1 & SH7750_SCSCR_TIE) { + fprintf(stderr, "interrupts for serial port 1 not implemented\n"); + assert(0); + } + /* XXXXX Check for errors in write */ + qemu_chr_write(s->serial1, &c, 1); +} + +static void serial1_change_scssr1(SH7750State * s, uint8_t mem_value) +{ + uint8_t new_flags; + + /* If transmit disable, TDRE and TEND stays up */ + if ((s->scscr1 & SH7750_SCSCR_TE) == 0) { + mem_value |= SH7750_SCSSR1_TDRE | SH7750_SCSSR1_TEND; + } + + /* Only clear bits which have been read before and do not set any bit + in the flags */ + new_flags = s->scssr1 & ~s->scssr1_read; /* Preserve unread flags */ + new_flags &= mem_value | ~s->scssr1_read; /* Clear read flags */ + + s->scssr1 = (new_flags & 0xf8) | (mem_value & 1); + s->scssr1_read &= mem_value; + + /* If TDRE has been cleared, TEND will also be cleared */ + if ((s->scssr1 & SH7750_SCSSR1_TDRE) == 0) { + s->scssr1 &= ~SH7750_SCSSR1_TEND; + } + + /* Check for transmission to start */ + serial1_maybe_send(s); +} + +static void serial1_update_parameters(SH7750State * s) +{ + QEMUSerialSetParams ssp; + + if (s->scsmr1 & SH7750_SCSMR_CHR_7) + ssp.data_bits = 7; + else + ssp.data_bits = 8; + if (s->scsmr1 & SH7750_SCSMR_PE) { + if (s->scsmr1 & SH7750_SCSMR_PM_ODD) + ssp.parity = 'O'; + else + ssp.parity = 'E'; + } else + ssp.parity = 'N'; + if (s->scsmr1 & SH7750_SCSMR_STOP_2) + ssp.stop_bits = 2; + else + ssp.stop_bits = 1; + fprintf(stderr, "SCSMR1=%04x SCBRR1=%02x\n", s->scsmr1, s->scbrr1); + ssp.speed = s->periph_freq / + (32 * s->scbrr1 * (1 << (2 * (s->scsmr1 & 3)))) - 1; + fprintf(stderr, "data bits=%d, stop bits=%d, parity=%c, speed=%d\n", + ssp.data_bits, ssp.stop_bits, ssp.parity, ssp.speed); + qemu_chr_ioctl(s->serial1, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); +} + +static void scscr1_changed(SH7750State * s) +{ + if (s->scscr1 & (SH7750_SCSCR_TE | SH7750_SCSCR_RE)) { + if (!s->serial1) { + fprintf(stderr, "serial port 1 not bound to anything\n"); + assert(0); + } + serial1_update_parameters(s); + } + if ((s->scscr1 & SH7750_SCSCR_RE) == 0) { + s->scssr1 |= SH7750_SCSSR1_TDRE; + } +} + +static void init_serial1(SH7750State * s, int serial_nb) +{ + CharDriverState *chr; + + s->scssr1 = 0x84; + chr = serial_hds[serial_nb]; + if (!chr) { + fprintf(stderr, + "no serial port associated to SH7750 first serial port\n"); + return; + } + + s->serial1 = chr; + qemu_chr_add_read_handler(chr, serial1_can_receive, + serial1_receive, s); + qemu_chr_add_event_handler(chr, serial1_event); +} + +/********************************************************************** + Second serial port +**********************************************************************/ + +static int serial2_can_receive(void *opaque) +{ + SH7750State *s = opaque; + static uint8_t max_fifo_size[] = { 15, 1, 4, 6, 8, 10, 12, 14 }; + + return s->serial2_receive_fifo.length < + max_fifo_size[(s->scfcr2 >> 9) & 7]; +} + +static void serial2_adjust_receive_flags(SH7750State * s) +{ + static uint8_t max_fifo_size[] = { 1, 4, 8, 14 }; + + /* XXXXX Add interrupt generation */ + if (s->serial2_receive_fifo.length >= + max_fifo_size[(s->scfcr2 >> 7) & 3]) { + s->scfsr2 |= SH7750_SCFSR2_RDF; + s->scfsr2 &= ~SH7750_SCFSR2_DR; + } else { + s->scfsr2 &= ~SH7750_SCFSR2_RDF; + if (s->serial2_receive_fifo.length > 0) + s->scfsr2 |= SH7750_SCFSR2_DR; + else + s->scfsr2 &= ~SH7750_SCFSR2_DR; + } +} + +static void serial2_append_char(SH7750State * s, uint8_t c) +{ + if (s->serial2_receive_fifo.length == 16) { + /* Overflow */ + s->sclsr2 |= SH7750_SCLSR2_ORER; + return; + } + + s->serial2_receive_fifo.data[s->serial2_receive_fifo.write_idx++] = c; + s->serial2_receive_fifo.length++; + serial2_adjust_receive_flags(s); +} + +static void serial2_receive(void *opaque, const uint8_t * buf, int size) +{ + SH7750State *s = opaque; + int i; + + for (i = 0; i < size; i++) + serial2_append_char(s, buf[i]); +} + +static void serial2_event(void *opaque, int event) +{ + /* XXXXX */ + assert(0); +} + +static void serial2_update_parameters(SH7750State * s) +{ + QEMUSerialSetParams ssp; + + if (s->scsmr2 & SH7750_SCSMR_CHR_7) + ssp.data_bits = 7; + else + ssp.data_bits = 8; + if (s->scsmr2 & SH7750_SCSMR_PE) { + if (s->scsmr2 & SH7750_SCSMR_PM_ODD) + ssp.parity = 'O'; + else + ssp.parity = 'E'; + } else + ssp.parity = 'N'; + if (s->scsmr2 & SH7750_SCSMR_STOP_2) + ssp.stop_bits = 2; + else + ssp.stop_bits = 1; + fprintf(stderr, "SCSMR2=%04x SCBRR2=%02x\n", s->scsmr2, s->scbrr2); + ssp.speed = s->periph_freq / + (32 * s->scbrr2 * (1 << (2 * (s->scsmr2 & 3)))) - 1; + fprintf(stderr, "data bits=%d, stop bits=%d, parity=%c, speed=%d\n", + ssp.data_bits, ssp.stop_bits, ssp.parity, ssp.speed); + qemu_chr_ioctl(s->serial2, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); +} + +static void scscr2_changed(SH7750State * s) +{ + if (s->scscr2 & (SH7750_SCSCR_TE | SH7750_SCSCR_RE)) { + if (!s->serial2) { + fprintf(stderr, "serial port 2 not bound to anything\n"); + assert(0); + } + serial2_update_parameters(s); + } +} + +static void init_serial2(SH7750State * s, int serial_nb) +{ + CharDriverState *chr; + + s->scfsr2 = 0x0060; + + chr = serial_hds[serial_nb]; + if (!chr) { + fprintf(stderr, + "no serial port associated to SH7750 second serial port\n"); + return; + } + + s->serial2 = chr; + qemu_chr_add_read_handler(chr, serial2_can_receive, + serial2_receive, s); + qemu_chr_add_event_handler(chr, serial2_event); +} + +static void init_serial_ports(SH7750State * s) +{ + init_serial1(s, 0); + init_serial2(s, 1); +} + +/********************************************************************** + I/O ports +**********************************************************************/ + +int sh7750_register_io_device(SH7750State * s, sh7750_io_device * device) +{ + int i; + + for (i = 0; i < NB_DEVICES; i++) { + if (s->devices[i] == NULL) { + s->devices[i] = device; + return 0; + } + } + return -1; +} + +static uint16_t portdir(uint32_t v) +{ +#define EVENPORTMASK(n) ((v & (1<<((n)<<1))) >> (n)) + return + EVENPORTMASK(15) | EVENPORTMASK(14) | EVENPORTMASK(13) | + EVENPORTMASK(12) | EVENPORTMASK(11) | EVENPORTMASK(10) | + EVENPORTMASK(9) | EVENPORTMASK(8) | EVENPORTMASK(7) | + EVENPORTMASK(6) | EVENPORTMASK(5) | EVENPORTMASK(4) | + EVENPORTMASK(3) | EVENPORTMASK(2) | EVENPORTMASK(1) | + EVENPORTMASK(0); +} + +static uint16_t portpullup(uint32_t v) +{ +#define ODDPORTMASK(n) ((v & (1<<(((n)<<1)+1))) >> (n)) + return + ODDPORTMASK(15) | ODDPORTMASK(14) | ODDPORTMASK(13) | + ODDPORTMASK(12) | ODDPORTMASK(11) | ODDPORTMASK(10) | + ODDPORTMASK(9) | ODDPORTMASK(8) | ODDPORTMASK(7) | ODDPORTMASK(6) | + ODDPORTMASK(5) | ODDPORTMASK(4) | ODDPORTMASK(3) | ODDPORTMASK(2) | + ODDPORTMASK(1) | ODDPORTMASK(0); +} + +static uint16_t porta_lines(SH7750State * s) +{ + return (s->portdira & s->pdtra) | /* CPU */ + (s->periph_portdira & s->periph_pdtra) | /* Peripherals */ + (~(s->portdira | s->periph_portdira) & s->portpullupa); /* Pullups */ +} + +static uint16_t portb_lines(SH7750State * s) +{ + return (s->portdirb & s->pdtrb) | /* CPU */ + (s->periph_portdirb & s->periph_pdtrb) | /* Peripherals */ + (~(s->portdirb | s->periph_portdirb) & s->portpullupb); /* Pullups */ +} + +static void gen_port_interrupts(SH7750State * s) +{ + /* XXXXX interrupts not generated */ +} + +static void porta_changed(SH7750State * s, uint16_t prev) +{ + uint16_t currenta, changes; + int i, r = 0; + +#if 0 + fprintf(stderr, "porta changed from 0x%04x to 0x%04x\n", + prev, porta_lines(s)); + fprintf(stderr, "pdtra=0x%04x, pctra=0x%08x\n", s->pdtra, s->pctra); +#endif + currenta = porta_lines(s); + if (currenta == prev) + return; + changes = currenta ^ prev; + + for (i = 0; i < NB_DEVICES; i++) { + if (s->devices[i] && (s->devices[i]->portamask_trigger & changes)) { + r |= s->devices[i]->port_change_cb(currenta, portb_lines(s), + &s->periph_pdtra, + &s->periph_portdira, + &s->periph_pdtrb, + &s->periph_portdirb); + } + } + + if (r) + gen_port_interrupts(s); +} + +static void portb_changed(SH7750State * s, uint16_t prev) +{ + uint16_t currentb, changes; + int i, r = 0; + + currentb = portb_lines(s); + if (currentb == prev) + return; + changes = currentb ^ prev; + + for (i = 0; i < NB_DEVICES; i++) { + if (s->devices[i] && (s->devices[i]->portbmask_trigger & changes)) { + r |= s->devices[i]->port_change_cb(portb_lines(s), currentb, + &s->periph_pdtra, + &s->periph_portdira, + &s->periph_pdtrb, + &s->periph_portdirb); + } + } + + if (r) + gen_port_interrupts(s); +} + +/********************************************************************** + Memory +**********************************************************************/ + +static void error_access(const char *kind, target_phys_addr_t addr) +{ + fprintf(stderr, "%s to %s (0x%08x) not supported\n", + kind, regname(addr), addr); +} + +static void ignore_access(const char *kind, target_phys_addr_t addr) +{ + fprintf(stderr, "%s to %s (0x%08x) ignored\n", + kind, regname(addr), addr); +} + +static uint32_t sh7750_mem_readb(void *opaque, target_phys_addr_t addr) +{ + SH7750State *s = opaque; + uint8_t r; + + switch (addr) { + case SH7750_SCSSR1_A7: + r = s->scssr1; + s->scssr1_read |= r; + return s->scssr1; + case SH7750_SCRDR1_A7: + s->scssr1 &= ~SH7750_SCSSR1_RDRF; + return s->scrdr1; + default: + error_access("byte read", addr); + assert(0); + } +} + +static uint32_t sh7750_mem_readw(void *opaque, target_phys_addr_t addr) +{ + SH7750State *s = opaque; + uint16_t r; + + switch (addr) { + case SH7750_RFCR_A7: + fprintf(stderr, + "Read access to refresh count register, incrementing\n"); + return s->rfcr++; + case SH7750_TCR0_A7: + return s->tcr0; + case SH7750_SCLSR2_A7: + /* Read and clear overflow bit */ + r = s->sclsr2; + s->sclsr2 = 0; + return r; + case SH7750_SCSFR2_A7: + return s->scfsr2; + case SH7750_PDTRA_A7: + return porta_lines(s); + case SH7750_PDTRB_A7: + return portb_lines(s); + default: + error_access("word read", addr); + assert(0); + } +} + +static uint32_t sh7750_mem_readl(void *opaque, target_phys_addr_t addr) +{ + SH7750State *s = opaque; + + switch (addr) { + case SH7750_MMUCR_A7: + return s->cpu->mmucr; + case SH7750_PTEH_A7: + return s->cpu->pteh; + case SH7750_PTEL_A7: + return s->cpu->ptel; + case SH7750_TTB_A7: + return s->cpu->ttb; + case SH7750_TEA_A7: + return s->cpu->tea; + case SH7750_TRA_A7: + return s->cpu->tra; + case SH7750_EXPEVT_A7: + return s->cpu->expevt; + case SH7750_INTEVT_A7: + return s->cpu->intevt; + case SH7750_CCR_A7: + return s->ccr; + case 0x1f000030: /* Processor version PVR */ + return 0x00050000; /* SH7750R */ + case 0x1f000040: /* Processor version CVR */ + return 0x00110000; /* Minimum caches */ + case 0x1f000044: /* Processor version PRR */ + return 0x00000100; /* SH7750R */ + default: + error_access("long read", addr); + assert(0); + } +} + +static void sh7750_mem_writeb(void *opaque, target_phys_addr_t addr, + uint32_t mem_value) +{ + SH7750State *s = opaque; + + switch (addr) { + /* PRECHARGE ? XXXXX */ + case SH7750_PRECHARGE0_A7: + case SH7750_PRECHARGE1_A7: + ignore_access("byte write", addr); + return; + case SH7750_SCBRR2_A7: + s->scbrr2 = mem_value; + return; + case SH7750_TSTR_A7: + s->tstr = mem_value; + timer_start_changed(s); + return; + case SH7750_SCSCR1_A7: + s->scscr1 = mem_value; + scscr1_changed(s); + return; + case SH7750_SCSMR1_A7: + s->scsmr1 = mem_value; + return; + case SH7750_SCBRR1_A7: + s->scbrr1 = mem_value; + return; + case SH7750_SCTDR1_A7: + s->scssr1 &= ~SH7750_SCSSR1_TEND; + s->sctdr1 = mem_value; + return; + case SH7750_SCSSR1_A7: + serial1_change_scssr1(s, mem_value); + return; + default: + error_access("byte write", addr); + assert(0); + } +} + +static void sh7750_mem_writew(void *opaque, target_phys_addr_t addr, + uint32_t mem_value) +{ + SH7750State *s = opaque; + uint16_t temp; + + switch (addr) { + /* SDRAM controller */ + case SH7750_SCBRR1_A7: + case SH7750_SCBRR2_A7: + case SH7750_BCR2_A7: + case SH7750_BCR3_A7: + case SH7750_RTCOR_A7: + case SH7750_RTCNT_A7: + case SH7750_RTCSR_A7: + ignore_access("word write", addr); + return; + /* IO ports */ + case SH7750_PDTRA_A7: + temp = porta_lines(s); + s->pdtra = mem_value; + porta_changed(s, temp); + return; + case SH7750_PDTRB_A7: + temp = portb_lines(s); + s->pdtrb = mem_value; + portb_changed(s, temp); + return; + case SH7750_RFCR_A7: + fprintf(stderr, "Write access to refresh count register\n"); + s->rfcr = mem_value; + return; + case SH7750_SCLSR2_A7: + s->sclsr2 = mem_value; + return; + case SH7750_SCSCR2_A7: + s->scscr2 = mem_value; + scscr2_changed(s); + return; + case SH7750_SCFCR2_A7: + s->scfcr2 = mem_value; + return; + case SH7750_SCSMR2_A7: + s->scsmr2 = mem_value; + return; + case SH7750_TCR0_A7: + s->tcr0 = mem_value; + return; + case SH7750_GPIOIC_A7: + s->gpioic = mem_value; + if (mem_value != 0) { + fprintf(stderr, "I/O interrupts not implemented\n"); + assert(0); + } + return; + default: + error_access("word write", addr); + assert(0); + } +} + +static void sh7750_mem_writel(void *opaque, target_phys_addr_t addr, + uint32_t mem_value) +{ + SH7750State *s = opaque; + uint16_t temp; + + switch (addr) { + /* SDRAM controller */ + case SH7750_BCR1_A7: + case SH7750_BCR4_A7: + case SH7750_WCR1_A7: + case SH7750_WCR2_A7: + case SH7750_WCR3_A7: + case SH7750_MCR_A7: + ignore_access("long write", addr); + return; + /* IO ports */ + case SH7750_PCTRA_A7: + temp = porta_lines(s); + s->pctra = mem_value; + s->portdira = portdir(mem_value); + s->portpullupa = portpullup(mem_value); + porta_changed(s, temp); + return; + case SH7750_PCTRB_A7: + temp = portb_lines(s); + s->pctrb = mem_value; + s->portdirb = portdir(mem_value); + s->portpullupb = portpullup(mem_value); + portb_changed(s, temp); + return; + case SH7750_TCNT0_A7: + s->tcnt0 = mem_value & 0xf; + return; + case SH7750_MMUCR_A7: + s->cpu->mmucr = mem_value; + return; + case SH7750_PTEH_A7: + s->cpu->pteh = mem_value; + return; + case SH7750_PTEL_A7: + s->cpu->ptel = mem_value; + return; + case SH7750_TTB_A7: + s->cpu->ttb = mem_value; + return; + case SH7750_TEA_A7: + s->cpu->tea = mem_value; + return; + case SH7750_TRA_A7: + s->cpu->tra = mem_value & 0x000007ff; + return; + case SH7750_EXPEVT_A7: + s->cpu->expevt = mem_value & 0x000007ff; + return; + case SH7750_INTEVT_A7: + s->cpu->intevt = mem_value & 0x000007ff; + return; + case SH7750_CCR_A7: + s->ccr = mem_value; + return; + default: + error_access("long write", addr); + assert(0); + } +} + +static CPUReadMemoryFunc *sh7750_mem_read[] = { + sh7750_mem_readb, + sh7750_mem_readw, + sh7750_mem_readl +}; + +static CPUWriteMemoryFunc *sh7750_mem_write[] = { + sh7750_mem_writeb, + sh7750_mem_writew, + sh7750_mem_writel +}; + +SH7750State *sh7750_init(CPUSH4State * cpu) +{ + SH7750State *s; + int sh7750_io_memory; + + s = qemu_mallocz(sizeof(SH7750State)); + s->cpu = cpu; + s->periph_freq = 60000000; /* 60MHz */ + sh7750_io_memory = cpu_register_io_memory(0, + sh7750_mem_read, + sh7750_mem_write, s); + cpu_register_physical_memory(0x1c000000, 0x04000000, sh7750_io_memory); + init_timers(s); + init_serial_ports(s); + return s; +} diff --git a/tools/ioemu/hw/sh7750_regnames.c b/tools/ioemu/hw/sh7750_regnames.c new file mode 100644 index 0000000000..5fcb0d6cca --- /dev/null +++ b/tools/ioemu/hw/sh7750_regnames.c @@ -0,0 +1,128 @@ +#include "vl.h" +#include "sh7750_regs.h" + +#define REGNAME(r) {r, #r}, + +typedef struct { + uint32_t regaddr; + const char *regname; +} regname_t; + +static regname_t regnames[] = { + REGNAME(SH7750_PTEH_A7) + REGNAME(SH7750_PTEL_A7) + REGNAME(SH7750_PTEA_A7) + REGNAME(SH7750_TTB_A7) + REGNAME(SH7750_TEA_A7) + REGNAME(SH7750_MMUCR_A7) + REGNAME(SH7750_CCR_A7) + REGNAME(SH7750_QACR0_A7) + REGNAME(SH7750_QACR1_A7) + REGNAME(SH7750_TRA_A7) + REGNAME(SH7750_EXPEVT_A7) + REGNAME(SH7750_INTEVT_A7) + REGNAME(SH7750_STBCR_A7) + REGNAME(SH7750_STBCR2_A7) + REGNAME(SH7750_FRQCR_A7) + REGNAME(SH7750_WTCNT_A7) + REGNAME(SH7750_WTCSR_A7) + REGNAME(SH7750_R64CNT_A7) + REGNAME(SH7750_RSECCNT_A7) + REGNAME(SH7750_RMINCNT_A7) + REGNAME(SH7750_RHRCNT_A7) + REGNAME(SH7750_RWKCNT_A7) + REGNAME(SH7750_RDAYCNT_A7) + REGNAME(SH7750_RMONCNT_A7) + REGNAME(SH7750_RYRCNT_A7) + REGNAME(SH7750_RSECAR_A7) + REGNAME(SH7750_RMINAR_A7) + REGNAME(SH7750_RHRAR_A7) + REGNAME(SH7750_RWKAR_A7) + REGNAME(SH7750_RDAYAR_A7) + REGNAME(SH7750_RMONAR_A7) + REGNAME(SH7750_RCR1_A7) + REGNAME(SH7750_RCR2_A7) + REGNAME(SH7750_TOCR_A7) + REGNAME(SH7750_TSTR_A7) + REGNAME(SH7750_TCOR0_A7) + REGNAME(SH7750_TCOR1_A7) + REGNAME(SH7750_TCOR2_A7) + REGNAME(SH7750_TCNT0_A7) + REGNAME(SH7750_TCNT1_A7) + REGNAME(SH7750_TCNT2_A7) + REGNAME(SH7750_TCR0_A7) + REGNAME(SH7750_TCR1_A7) + REGNAME(SH7750_TCR2_A7) + REGNAME(SH7750_TCPR2_A7) + REGNAME(SH7750_BCR1_A7) + REGNAME(SH7750_BCR2_A7) + REGNAME(SH7750_WCR1_A7) + REGNAME(SH7750_WCR2_A7) + REGNAME(SH7750_WCR3_A7) + REGNAME(SH7750_MCR_A7) + REGNAME(SH7750_PCR_A7) + REGNAME(SH7750_RTCSR_A7) + REGNAME(SH7750_RTCNT_A7) + REGNAME(SH7750_RTCOR_A7) + REGNAME(SH7750_RFCR_A7) + REGNAME(SH7750_SAR0_A7) + REGNAME(SH7750_SAR1_A7) + REGNAME(SH7750_SAR2_A7) + REGNAME(SH7750_SAR3_A7) + REGNAME(SH7750_DAR0_A7) + REGNAME(SH7750_DAR1_A7) + REGNAME(SH7750_DAR2_A7) + REGNAME(SH7750_DAR3_A7) + REGNAME(SH7750_DMATCR0_A7) + REGNAME(SH7750_DMATCR1_A7) + REGNAME(SH7750_DMATCR2_A7) + REGNAME(SH7750_DMATCR3_A7) + REGNAME(SH7750_CHCR0_A7) + REGNAME(SH7750_CHCR1_A7) + REGNAME(SH7750_CHCR2_A7) + REGNAME(SH7750_CHCR3_A7) + REGNAME(SH7750_DMAOR_A7) + REGNAME(SH7750_SCRDR1_A7) + REGNAME(SH7750_SCRDR2_A7) + REGNAME(SH7750_SCTDR1_A7) + REGNAME(SH7750_SCTDR2_A7) + REGNAME(SH7750_SCSMR1_A7) + REGNAME(SH7750_SCSMR2_A7) + REGNAME(SH7750_SCSCR1_A7) + REGNAME(SH7750_SCSCR2_A7) + REGNAME(SH7750_SCSSR1_A7) + REGNAME(SH7750_SCSFR2_A7) + REGNAME(SH7750_SCSPTR1_A7) + REGNAME(SH7750_SCSPTR2_A7) + REGNAME(SH7750_SCBRR1_A7) + REGNAME(SH7750_SCBRR2_A7) + REGNAME(SH7750_SCFCR2_A7) + REGNAME(SH7750_SCFDR2_A7) + REGNAME(SH7750_SCLSR2_A7) + REGNAME(SH7750_SCSCMR1_A7) + REGNAME(SH7750_PCTRA_A7) + REGNAME(SH7750_PDTRA_A7) + REGNAME(SH7750_PCTRB_A7) + REGNAME(SH7750_PDTRB_A7) + REGNAME(SH7750_GPIOIC_A7) + REGNAME(SH7750_ICR_A7) + REGNAME(SH7750_IPRA_A7) + REGNAME(SH7750_IPRB_A7) + REGNAME(SH7750_IPRC_A7) + REGNAME(SH7750_BCR3_A7) + REGNAME(SH7750_BCR4_A7) + REGNAME(SH7750_PRECHARGE0_A7) + REGNAME(SH7750_PRECHARGE1_A7) {(uint32_t) - 1, 0} +}; + +const char *regname(uint32_t addr) +{ + unsigned int i; + + for (i = 0; regnames[i].regaddr != (uint32_t) - 1; i++) { + if (regnames[i].regaddr == addr) + return regnames[i].regname; + } + + return ""; +} diff --git a/tools/ioemu/hw/sh7750_regnames.h b/tools/ioemu/hw/sh7750_regnames.h new file mode 100644 index 0000000000..7463709b4c --- /dev/null +++ b/tools/ioemu/hw/sh7750_regnames.h @@ -0,0 +1,6 @@ +#ifndef _SH7750_REGNAMES_H +#define _SH7750_REGNAMES_H + +const char *regname(uint32_t addr); + +#endif /* _SH7750_REGNAMES_H */ diff --git a/tools/ioemu/hw/sh7750_regs.h b/tools/ioemu/hw/sh7750_regs.h new file mode 100644 index 0000000000..44ae95be2b --- /dev/null +++ b/tools/ioemu/hw/sh7750_regs.h @@ -0,0 +1,1623 @@ +/* + * SH-7750 memory-mapped registers + * This file based on information provided in the following document: + * "Hitachi SuperH (tm) RISC engine. SH7750 Series (SH7750, SH7750S) + * Hardware Manual" + * Document Number ADE-602-124C, Rev. 4.0, 4/21/00, Hitachi Ltd. + * + * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia + * Author: Alexandra Kossovsky + * Victor V. Vengerov + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * @(#) sh7750_regs.h,v 1.2.4.1 2003/09/04 18:46:00 joel Exp + */ + +#ifndef __SH7750_REGS_H__ +#define __SH7750_REGS_H__ + +/* + * All register has 2 addresses: in 0xff000000 - 0xffffffff (P4 address) and + * in 0x1f000000 - 0x1fffffff (area 7 address) + */ +#define SH7750_P4_BASE 0xff000000 /* Accessable only in + priveleged mode */ +#define SH7750_A7_BASE 0x1f000000 /* Accessable only using TLB */ + +#define SH7750_P4_REG32(ofs) (SH7750_P4_BASE + (ofs)) +#define SH7750_A7_REG32(ofs) (SH7750_A7_BASE + (ofs)) + +/* + * MMU Registers + */ + +/* Page Table Entry High register - PTEH */ +#define SH7750_PTEH_REGOFS 0x000000 /* offset */ +#define SH7750_PTEH SH7750_P4_REG32(SH7750_PTEH_REGOFS) +#define SH7750_PTEH_A7 SH7750_A7_REG32(SH7750_PTEH_REGOFS) +#define SH7750_PTEH_VPN 0xfffffd00 /* Virtual page number */ +#define SH7750_PTEH_VPN_S 10 +#define SH7750_PTEH_ASID 0x000000ff /* Address space identifier */ +#define SH7750_PTEH_ASID_S 0 + +/* Page Table Entry Low register - PTEL */ +#define SH7750_PTEL_REGOFS 0x000004 /* offset */ +#define SH7750_PTEL SH7750_P4_REG32(SH7750_PTEL_REGOFS) +#define SH7750_PTEL_A7 SH7750_A7_REG32(SH7750_PTEL_REGOFS) +#define SH7750_PTEL_PPN 0x1ffffc00 /* Physical page number */ +#define SH7750_PTEL_PPN_S 10 +#define SH7750_PTEL_V 0x00000100 /* Validity (0-entry is invalid) */ +#define SH7750_PTEL_SZ1 0x00000080 /* Page size bit 1 */ +#define SH7750_PTEL_SZ0 0x00000010 /* Page size bit 0 */ +#define SH7750_PTEL_SZ_1KB 0x00000000 /* 1-kbyte page */ +#define SH7750_PTEL_SZ_4KB 0x00000010 /* 4-kbyte page */ +#define SH7750_PTEL_SZ_64KB 0x00000080 /* 64-kbyte page */ +#define SH7750_PTEL_SZ_1MB 0x00000090 /* 1-Mbyte page */ +#define SH7750_PTEL_PR 0x00000060 /* Protection Key Data */ +#define SH7750_PTEL_PR_ROPO 0x00000000 /* read-only in priv mode */ +#define SH7750_PTEL_PR_RWPO 0x00000020 /* read-write in priv mode */ +#define SH7750_PTEL_PR_ROPU 0x00000040 /* read-only in priv or user mode */ +#define SH7750_PTEL_PR_RWPU 0x00000060 /* read-write in priv or user mode */ +#define SH7750_PTEL_C 0x00000008 /* Cacheability + (0 - page not cacheable) */ +#define SH7750_PTEL_D 0x00000004 /* Dirty bit (1 - write has been + performed to a page) */ +#define SH7750_PTEL_SH 0x00000002 /* Share Status bit (1 - page are + shared by processes) */ +#define SH7750_PTEL_WT 0x00000001 /* Write-through bit, specifies the + cache write mode: + 0 - Copy-back mode + 1 - Write-through mode */ + +/* Page Table Entry Assistance register - PTEA */ +#define SH7750_PTEA_REGOFS 0x000034 /* offset */ +#define SH7750_PTEA SH7750_P4_REG32(SH7750_PTEA_REGOFS) +#define SH7750_PTEA_A7 SH7750_A7_REG32(SH7750_PTEA_REGOFS) +#define SH7750_PTEA_TC 0x00000008 /* Timing Control bit + 0 - use area 5 wait states + 1 - use area 6 wait states */ +#define SH7750_PTEA_SA 0x00000007 /* Space Attribute bits: */ +#define SH7750_PTEA_SA_UNDEF 0x00000000 /* 0 - undefined */ +#define SH7750_PTEA_SA_IOVAR 0x00000001 /* 1 - variable-size I/O space */ +#define SH7750_PTEA_SA_IO8 0x00000002 /* 2 - 8-bit I/O space */ +#define SH7750_PTEA_SA_IO16 0x00000003 /* 3 - 16-bit I/O space */ +#define SH7750_PTEA_SA_CMEM8 0x00000004 /* 4 - 8-bit common memory space */ +#define SH7750_PTEA_SA_CMEM16 0x00000005 /* 5 - 16-bit common memory space */ +#define SH7750_PTEA_SA_AMEM8 0x00000006 /* 6 - 8-bit attr memory space */ +#define SH7750_PTEA_SA_AMEM16 0x00000007 /* 7 - 16-bit attr memory space */ + + +/* Translation table base register */ +#define SH7750_TTB_REGOFS 0x000008 /* offset */ +#define SH7750_TTB SH7750_P4_REG32(SH7750_TTB_REGOFS) +#define SH7750_TTB_A7 SH7750_A7_REG32(SH7750_TTB_REGOFS) + +/* TLB exeption address register - TEA */ +#define SH7750_TEA_REGOFS 0x00000c /* offset */ +#define SH7750_TEA SH7750_P4_REG32(SH7750_TEA_REGOFS) +#define SH7750_TEA_A7 SH7750_A7_REG32(SH7750_TEA_REGOFS) + +/* MMU control register - MMUCR */ +#define SH7750_MMUCR_REGOFS 0x000010 /* offset */ +#define SH7750_MMUCR SH7750_P4_REG32(SH7750_MMUCR_REGOFS) +#define SH7750_MMUCR_A7 SH7750_A7_REG32(SH7750_MMUCR_REGOFS) +#define SH7750_MMUCR_AT 0x00000001 /* Address translation bit */ +#define SH7750_MMUCR_TI 0x00000004 /* TLB invalidate */ +#define SH7750_MMUCR_SV 0x00000100 /* Single Virtual Mode bit */ +#define SH7750_MMUCR_SQMD 0x00000200 /* Store Queue Mode bit */ +#define SH7750_MMUCR_URC 0x0000FC00 /* UTLB Replace Counter */ +#define SH7750_MMUCR_URC_S 10 +#define SH7750_MMUCR_URB 0x00FC0000 /* UTLB Replace Boundary */ +#define SH7750_MMUCR_URB_S 18 +#define SH7750_MMUCR_LRUI 0xFC000000 /* Least Recently Used ITLB */ +#define SH7750_MMUCR_LRUI_S 26 + + + + +/* + * Cache registers + * IC -- instructions cache + * OC -- operand cache + */ + +/* Cache Control Register - CCR */ +#define SH7750_CCR_REGOFS 0x00001c /* offset */ +#define SH7750_CCR SH7750_P4_REG32(SH7750_CCR_REGOFS) +#define SH7750_CCR_A7 SH7750_A7_REG32(SH7750_CCR_REGOFS) + +#define SH7750_CCR_IIX 0x00008000 /* IC index enable bit */ +#define SH7750_CCR_ICI 0x00000800 /* IC invalidation bit: + set it to clear IC */ +#define SH7750_CCR_ICE 0x00000100 /* IC enable bit */ +#define SH7750_CCR_OIX 0x00000080 /* OC index enable bit */ +#define SH7750_CCR_ORA 0x00000020 /* OC RAM enable bit + if you set OCE = 0, + you should set ORA = 0 */ +#define SH7750_CCR_OCI 0x00000008 /* OC invalidation bit */ +#define SH7750_CCR_CB 0x00000004 /* Copy-back bit for P1 area */ +#define SH7750_CCR_WT 0x00000002 /* Write-through bit for P0,U0,P3 area */ +#define SH7750_CCR_OCE 0x00000001 /* OC enable bit */ + +/* Queue address control register 0 - QACR0 */ +#define SH7750_QACR0_REGOFS 0x000038 /* offset */ +#define SH7750_QACR0 SH7750_P4_REG32(SH7750_QACR0_REGOFS) +#define SH7750_QACR0_A7 SH7750_A7_REG32(SH7750_QACR0_REGOFS) + +/* Queue address control register 1 - QACR1 */ +#define SH7750_QACR1_REGOFS 0x00003c /* offset */ +#define SH7750_QACR1 SH7750_P4_REG32(SH7750_QACR1_REGOFS) +#define SH7750_QACR1_A7 SH7750_A7_REG32(SH7750_QACR1_REGOFS) + + +/* + * Exeption-related registers + */ + +/* Immediate data for TRAPA instuction - TRA */ +#define SH7750_TRA_REGOFS 0x000020 /* offset */ +#define SH7750_TRA SH7750_P4_REG32(SH7750_TRA_REGOFS) +#define SH7750_TRA_A7 SH7750_A7_REG32(SH7750_TRA_REGOFS) + +#define SH7750_TRA_IMM 0x000003fd /* Immediate data operand */ +#define SH7750_TRA_IMM_S 2 + +/* Exeption event register - EXPEVT */ +#define SH7750_EXPEVT_REGOFS 0x000024 +#define SH7750_EXPEVT SH7750_P4_REG32(SH7750_EXPEVT_REGOFS) +#define SH7750_EXPEVT_A7 SH7750_A7_REG32(SH7750_EXPEVT_REGOFS) + +#define SH7750_EXPEVT_EX 0x00000fff /* Exeption code */ +#define SH7750_EXPEVT_EX_S 0 + +/* Interrupt event register */ +#define SH7750_INTEVT_REGOFS 0x000028 +#define SH7750_INTEVT SH7750_P4_REG32(SH7750_INTEVT_REGOFS) +#define SH7750_INTEVT_A7 SH7750_A7_REG32(SH7750_INTEVT_REGOFS) +#define SH7750_INTEVT_EX 0x00000fff /* Exeption code */ +#define SH7750_INTEVT_EX_S 0 + +/* + * Exception/interrupt codes + */ +#define SH7750_EVT_TO_NUM(evt) ((evt) >> 5) + +/* Reset exception category */ +#define SH7750_EVT_POWER_ON_RST 0x000 /* Power-on reset */ +#define SH7750_EVT_MANUAL_RST 0x020 /* Manual reset */ +#define SH7750_EVT_TLB_MULT_HIT 0x140 /* TLB multiple-hit exception */ + +/* General exception category */ +#define SH7750_EVT_USER_BREAK 0x1E0 /* User break */ +#define SH7750_EVT_IADDR_ERR 0x0E0 /* Instruction address error */ +#define SH7750_EVT_TLB_READ_MISS 0x040 /* ITLB miss exception / + DTLB miss exception (read) */ +#define SH7750_EVT_TLB_READ_PROTV 0x0A0 /* ITLB protection violation / + DTLB protection violation (read) */ +#define SH7750_EVT_ILLEGAL_INSTR 0x180 /* General Illegal Instruction + exception */ +#define SH7750_EVT_SLOT_ILLEGAL_INSTR 0x1A0 /* Slot Illegal Instruction + exception */ +#define SH7750_EVT_FPU_DISABLE 0x800 /* General FPU disable exception */ +#define SH7750_EVT_SLOT_FPU_DISABLE 0x820 /* Slot FPU disable exception */ +#define SH7750_EVT_DATA_READ_ERR 0x0E0 /* Data address error (read) */ +#define SH7750_EVT_DATA_WRITE_ERR 0x100 /* Data address error (write) */ +#define SH7750_EVT_DTLB_WRITE_MISS 0x060 /* DTLB miss exception (write) */ +#define SH7750_EVT_DTLB_WRITE_PROTV 0x0C0 /* DTLB protection violation + exception (write) */ +#define SH7750_EVT_FPU_EXCEPTION 0x120 /* FPU exception */ +#define SH7750_EVT_INITIAL_PGWRITE 0x080 /* Initial Page Write exception */ +#define SH7750_EVT_TRAPA 0x160 /* Unconditional trap (TRAPA) */ + +/* Interrupt exception category */ +#define SH7750_EVT_NMI 0x1C0 /* Non-maskable interrupt */ +#define SH7750_EVT_IRQ0 0x200 /* External Interrupt 0 */ +#define SH7750_EVT_IRQ1 0x220 /* External Interrupt 1 */ +#define SH7750_EVT_IRQ2 0x240 /* External Interrupt 2 */ +#define SH7750_EVT_IRQ3 0x260 /* External Interrupt 3 */ +#define SH7750_EVT_IRQ4 0x280 /* External Interrupt 4 */ +#define SH7750_EVT_IRQ5 0x2A0 /* External Interrupt 5 */ +#define SH7750_EVT_IRQ6 0x2C0 /* External Interrupt 6 */ +#define SH7750_EVT_IRQ7 0x2E0 /* External Interrupt 7 */ +#define SH7750_EVT_IRQ8 0x300 /* External Interrupt 8 */ +#define SH7750_EVT_IRQ9 0x320 /* External Interrupt 9 */ +#define SH7750_EVT_IRQA 0x340 /* External Interrupt A */ +#define SH7750_EVT_IRQB 0x360 /* External Interrupt B */ +#define SH7750_EVT_IRQC 0x380 /* External Interrupt C */ +#define SH7750_EVT_IRQD 0x3A0 /* External Interrupt D */ +#define SH7750_EVT_IRQE 0x3C0 /* External Interrupt E */ + +/* Peripheral Module Interrupts - Timer Unit (TMU) */ +#define SH7750_EVT_TUNI0 0x400 /* TMU Underflow Interrupt 0 */ +#define SH7750_EVT_TUNI1 0x420 /* TMU Underflow Interrupt 1 */ +#define SH7750_EVT_TUNI2 0x440 /* TMU Underflow Interrupt 2 */ +#define SH7750_EVT_TICPI2 0x460 /* TMU Input Capture Interrupt 2 */ + +/* Peripheral Module Interrupts - Real-Time Clock (RTC) */ +#define SH7750_EVT_RTC_ATI 0x480 /* Alarm Interrupt Request */ +#define SH7750_EVT_RTC_PRI 0x4A0 /* Periodic Interrupt Request */ +#define SH7750_EVT_RTC_CUI 0x4C0 /* Carry Interrupt Request */ + +/* Peripheral Module Interrupts - Serial Communication Interface (SCI) */ +#define SH7750_EVT_SCI_ERI 0x4E0 /* Receive Error */ +#define SH7750_EVT_SCI_RXI 0x500 /* Receive Data Register Full */ +#define SH7750_EVT_SCI_TXI 0x520 /* Transmit Data Register Empty */ +#define SH7750_EVT_SCI_TEI 0x540 /* Transmit End */ + +/* Peripheral Module Interrupts - Watchdog Timer (WDT) */ +#define SH7750_EVT_WDT_ITI 0x560 /* Interval Timer Interrupt + (used when WDT operates in + interval timer mode) */ + +/* Peripheral Module Interrupts - Memory Refresh Unit (REF) */ +#define SH7750_EVT_REF_RCMI 0x580 /* Compare-match Interrupt */ +#define SH7750_EVT_REF_ROVI 0x5A0 /* Refresh Counter Overflow + interrupt */ + +/* Peripheral Module Interrupts - Hitachi User Debug Interface (H-UDI) */ +#define SH7750_EVT_HUDI 0x600 /* UDI interrupt */ + +/* Peripheral Module Interrupts - General-Purpose I/O (GPIO) */ +#define SH7750_EVT_GPIO 0x620 /* GPIO Interrupt */ + +/* Peripheral Module Interrupts - DMA Controller (DMAC) */ +#define SH7750_EVT_DMAC_DMTE0 0x640 /* DMAC 0 Transfer End Interrupt */ +#define SH7750_EVT_DMAC_DMTE1 0x660 /* DMAC 1 Transfer End Interrupt */ +#define SH7750_EVT_DMAC_DMTE2 0x680 /* DMAC 2 Transfer End Interrupt */ +#define SH7750_EVT_DMAC_DMTE3 0x6A0 /* DMAC 3 Transfer End Interrupt */ +#define SH7750_EVT_DMAC_DMAE 0x6C0 /* DMAC Address Error Interrupt */ + +/* Peripheral Module Interrupts - Serial Communication Interface with FIFO */ +/* (SCIF) */ +#define SH7750_EVT_SCIF_ERI 0x700 /* Receive Error */ +#define SH7750_EVT_SCIF_RXI 0x720 /* Receive FIFO Data Full or + Receive Data ready interrupt */ +#define SH7750_EVT_SCIF_BRI 0x740 /* Break or overrun error */ +#define SH7750_EVT_SCIF_TXI 0x760 /* Transmit FIFO Data Empty */ + +/* + * Power Management + */ +#define SH7750_STBCR_REGOFS 0xC00004 /* offset */ +#define SH7750_STBCR SH7750_P4_REG32(SH7750_STBCR_REGOFS) +#define SH7750_STBCR_A7 SH7750_A7_REG32(SH7750_STBCR_REGOFS) + +#define SH7750_STBCR_STBY 0x80 /* Specifies a transition to standby mode: + 0 - Transition to SLEEP mode on SLEEP + 1 - Transition to STANDBY mode on SLEEP */ +#define SH7750_STBCR_PHZ 0x40 /* State of peripheral module pins in + standby mode: + 0 - normal state + 1 - high-impendance state */ + +#define SH7750_STBCR_PPU 0x20 /* Peripheral module pins pull-up controls */ +#define SH7750_STBCR_MSTP4 0x10 /* Stopping the clock supply to DMAC */ +#define SH7750_STBCR_DMAC_STP SH7750_STBCR_MSTP4 +#define SH7750_STBCR_MSTP3 0x08 /* Stopping the clock supply to SCIF */ +#define SH7750_STBCR_SCIF_STP SH7750_STBCR_MSTP3 +#define SH7750_STBCR_MSTP2 0x04 /* Stopping the clock supply to TMU */ +#define SH7750_STBCR_TMU_STP SH7750_STBCR_MSTP2 +#define SH7750_STBCR_MSTP1 0x02 /* Stopping the clock supply to RTC */ +#define SH7750_STBCR_RTC_STP SH7750_STBCR_MSTP1 +#define SH7750_STBCR_MSPT0 0x01 /* Stopping the clock supply to SCI */ +#define SH7750_STBCR_SCI_STP SH7750_STBCR_MSTP0 + +#define SH7750_STBCR_STBY 0x80 + + +#define SH7750_STBCR2_REGOFS 0xC00010 /* offset */ +#define SH7750_STBCR2 SH7750_P4_REG32(SH7750_STBCR2_REGOFS) +#define SH7750_STBCR2_A7 SH7750_A7_REG32(SH7750_STBCR2_REGOFS) + +#define SH7750_STBCR2_DSLP 0x80 /* Specifies transition to deep sleep mode: + 0 - transition to sleep or standby mode + as it is specified in STBY bit + 1 - transition to deep sleep mode on + execution of SLEEP instruction */ +#define SH7750_STBCR2_MSTP6 0x02 /* Stopping the clock supply to Store Queue + in the cache controller */ +#define SH7750_STBCR2_SQ_STP SH7750_STBCR2_MSTP6 +#define SH7750_STBCR2_MSTP5 0x01 /* Stopping the clock supply to the User + Break Controller (UBC) */ +#define SH7750_STBCR2_UBC_STP SH7750_STBCR2_MSTP5 + +/* + * Clock Pulse Generator (CPG) + */ +#define SH7750_FRQCR_REGOFS 0xC00000 /* offset */ +#define SH7750_FRQCR SH7750_P4_REG32(SH7750_FRQCR_REGOFS) +#define SH7750_FRQCR_A7 SH7750_A7_REG32(SH7750_FRQCR_REGOFS) + +#define SH7750_FRQCR_CKOEN 0x0800 /* Clock Output Enable + 0 - CKIO pin goes to HiZ/pullup + 1 - Clock is output from CKIO */ +#define SH7750_FRQCR_PLL1EN 0x0400 /* PLL circuit 1 enable */ +#define SH7750_FRQCR_PLL2EN 0x0200 /* PLL circuit 2 enable */ + +#define SH7750_FRQCR_IFC 0x01C0 /* CPU clock frequency division ratio: */ +#define SH7750_FRQCR_IFCDIV1 0x0000 /* 0 - * 1 */ +#define SH7750_FRQCR_IFCDIV2 0x0040 /* 1 - * 1/2 */ +#define SH7750_FRQCR_IFCDIV3 0x0080 /* 2 - * 1/3 */ +#define SH7750_FRQCR_IFCDIV4 0x00C0 /* 3 - * 1/4 */ +#define SH7750_FRQCR_IFCDIV6 0x0100 /* 4 - * 1/6 */ +#define SH7750_FRQCR_IFCDIV8 0x0140 /* 5 - * 1/8 */ + +#define SH7750_FRQCR_BFC 0x0038 /* Bus clock frequency division ratio: */ +#define SH7750_FRQCR_BFCDIV1 0x0000 /* 0 - * 1 */ +#define SH7750_FRQCR_BFCDIV2 0x0008 /* 1 - * 1/2 */ +#define SH7750_FRQCR_BFCDIV3 0x0010 /* 2 - * 1/3 */ +#define SH7750_FRQCR_BFCDIV4 0x0018 /* 3 - * 1/4 */ +#define SH7750_FRQCR_BFCDIV6 0x0020 /* 4 - * 1/6 */ +#define SH7750_FRQCR_BFCDIV8 0x0028 /* 5 - * 1/8 */ + +#define SH7750_FRQCR_PFC 0x0007 /* Peripheral module clock frequency + division ratio: */ +#define SH7750_FRQCR_PFCDIV2 0x0000 /* 0 - * 1/2 */ +#define SH7750_FRQCR_PFCDIV3 0x0001 /* 1 - * 1/3 */ +#define SH7750_FRQCR_PFCDIV4 0x0002 /* 2 - * 1/4 */ +#define SH7750_FRQCR_PFCDIV6 0x0003 /* 3 - * 1/6 */ +#define SH7750_FRQCR_PFCDIV8 0x0004 /* 4 - * 1/8 */ + +/* + * Watchdog Timer (WDT) + */ + +/* Watchdog Timer Counter register - WTCNT */ +#define SH7750_WTCNT_REGOFS 0xC00008 /* offset */ +#define SH7750_WTCNT SH7750_P4_REG32(SH7750_WTCNT_REGOFS) +#define SH7750_WTCNT_A7 SH7750_A7_REG32(SH7750_WTCNT_REGOFS) +#define SH7750_WTCNT_KEY 0x5A00 /* When WTCNT byte register written, + you have to set the upper byte to + 0x5A */ + +/* Watchdog Timer Control/Status register - WTCSR */ +#define SH7750_WTCSR_REGOFS 0xC0000C /* offset */ +#define SH7750_WTCSR SH7750_P4_REG32(SH7750_WTCSR_REGOFS) +#define SH7750_WTCSR_A7 SH7750_A7_REG32(SH7750_WTCSR_REGOFS) +#define SH7750_WTCSR_KEY 0xA500 /* When WTCSR byte register written, + you have to set the upper byte to + 0xA5 */ +#define SH7750_WTCSR_TME 0x80 /* Timer enable (1-upcount start) */ +#define SH7750_WTCSR_MODE 0x40 /* Timer Mode Select: */ +#define SH7750_WTCSR_MODE_WT 0x40 /* Watchdog Timer Mode */ +#define SH7750_WTCSR_MODE_IT 0x00 /* Interval Timer Mode */ +#define SH7750_WTCSR_RSTS 0x20 /* Reset Select: */ +#define SH7750_WTCSR_RST_MAN 0x20 /* Manual Reset */ +#define SH7750_WTCSR_RST_PWR 0x00 /* Power-on Reset */ +#define SH7750_WTCSR_WOVF 0x10 /* Watchdog Timer Overflow Flag */ +#define SH7750_WTCSR_IOVF 0x08 /* Interval Timer Overflow Flag */ +#define SH7750_WTCSR_CKS 0x07 /* Clock Select: */ +#define SH7750_WTCSR_CKS_DIV32 0x00 /* 1/32 of frequency divider 2 input */ +#define SH7750_WTCSR_CKS_DIV64 0x01 /* 1/64 */ +#define SH7750_WTCSR_CKS_DIV128 0x02 /* 1/128 */ +#define SH7750_WTCSR_CKS_DIV256 0x03 /* 1/256 */ +#define SH7750_WTCSR_CKS_DIV512 0x04 /* 1/512 */ +#define SH7750_WTCSR_CKS_DIV1024 0x05 /* 1/1024 */ +#define SH7750_WTCSR_CKS_DIV2048 0x06 /* 1/2048 */ +#define SH7750_WTCSR_CKS_DIV4096 0x07 /* 1/4096 */ + +/* + * Real-Time Clock (RTC) + */ +/* 64-Hz Counter Register (byte, read-only) - R64CNT */ +#define SH7750_R64CNT_REGOFS 0xC80000 /* offset */ +#define SH7750_R64CNT SH7750_P4_REG32(SH7750_R64CNT_REGOFS) +#define SH7750_R64CNT_A7 SH7750_A7_REG32(SH7750_R64CNT_REGOFS) + +/* Second Counter Register (byte, BCD-coded) - RSECCNT */ +#define SH7750_RSECCNT_REGOFS 0xC80004 /* offset */ +#define SH7750_RSECCNT SH7750_P4_REG32(SH7750_RSECCNT_REGOFS) +#define SH7750_RSECCNT_A7 SH7750_A7_REG32(SH7750_RSECCNT_REGOFS) + +/* Minute Counter Register (byte, BCD-coded) - RMINCNT */ +#define SH7750_RMINCNT_REGOFS 0xC80008 /* offset */ +#define SH7750_RMINCNT SH7750_P4_REG32(SH7750_RMINCNT_REGOFS) +#define SH7750_RMINCNT_A7 SH7750_A7_REG32(SH7750_RMINCNT_REGOFS) + +/* Hour Counter Register (byte, BCD-coded) - RHRCNT */ +#define SH7750_RHRCNT_REGOFS 0xC8000C /* offset */ +#define SH7750_RHRCNT SH7750_P4_REG32(SH7750_RHRCNT_REGOFS) +#define SH7750_RHRCNT_A7 SH7750_A7_REG32(SH7750_RHRCNT_REGOFS) + +/* Day-of-Week Counter Register (byte) - RWKCNT */ +#define SH7750_RWKCNT_REGOFS 0xC80010 /* offset */ +#define SH7750_RWKCNT SH7750_P4_REG32(SH7750_RWKCNT_REGOFS) +#define SH7750_RWKCNT_A7 SH7750_A7_REG32(SH7750_RWKCNT_REGOFS) + +#define SH7750_RWKCNT_SUN 0 /* Sunday */ +#define SH7750_RWKCNT_MON 1 /* Monday */ +#define SH7750_RWKCNT_TUE 2 /* Tuesday */ +#define SH7750_RWKCNT_WED 3 /* Wednesday */ +#define SH7750_RWKCNT_THU 4 /* Thursday */ +#define SH7750_RWKCNT_FRI 5 /* Friday */ +#define SH7750_RWKCNT_SAT 6 /* Saturday */ + +/* Day Counter Register (byte, BCD-coded) - RDAYCNT */ +#define SH7750_RDAYCNT_REGOFS 0xC80014 /* offset */ +#define SH7750_RDAYCNT SH7750_P4_REG32(SH7750_RDAYCNT_REGOFS) +#define SH7750_RDAYCNT_A7 SH7750_A7_REG32(SH7750_RDAYCNT_REGOFS) + +/* Month Counter Register (byte, BCD-coded) - RMONCNT */ +#define SH7750_RMONCNT_REGOFS 0xC80018 /* offset */ +#define SH7750_RMONCNT SH7750_P4_REG32(SH7750_RMONCNT_REGOFS) +#define SH7750_RMONCNT_A7 SH7750_A7_REG32(SH7750_RMONCNT_REGOFS) + +/* Year Counter Register (half, BCD-coded) - RYRCNT */ +#define SH7750_RYRCNT_REGOFS 0xC8001C /* offset */ +#define SH7750_RYRCNT SH7750_P4_REG32(SH7750_RYRCNT_REGOFS) +#define SH7750_RYRCNT_A7 SH7750_A7_REG32(SH7750_RYRCNT_REGOFS) + +/* Second Alarm Register (byte, BCD-coded) - RSECAR */ +#define SH7750_RSECAR_REGOFS 0xC80020 /* offset */ +#define SH7750_RSECAR SH7750_P4_REG32(SH7750_RSECAR_REGOFS) +#define SH7750_RSECAR_A7 SH7750_A7_REG32(SH7750_RSECAR_REGOFS) +#define SH7750_RSECAR_ENB 0x80 /* Second Alarm Enable */ + +/* Minute Alarm Register (byte, BCD-coded) - RMINAR */ +#define SH7750_RMINAR_REGOFS 0xC80024 /* offset */ +#define SH7750_RMINAR SH7750_P4_REG32(SH7750_RMINAR_REGOFS) +#define SH7750_RMINAR_A7 SH7750_A7_REG32(SH7750_RMINAR_REGOFS) +#define SH7750_RMINAR_ENB 0x80 /* Minute Alarm Enable */ + +/* Hour Alarm Register (byte, BCD-coded) - RHRAR */ +#define SH7750_RHRAR_REGOFS 0xC80028 /* offset */ +#define SH7750_RHRAR SH7750_P4_REG32(SH7750_RHRAR_REGOFS) +#define SH7750_RHRAR_A7 SH7750_A7_REG32(SH7750_RHRAR_REGOFS) +#define SH7750_RHRAR_ENB 0x80 /* Hour Alarm Enable */ + +/* Day-of-Week Alarm Register (byte) - RWKAR */ +#define SH7750_RWKAR_REGOFS 0xC8002C /* offset */ +#define SH7750_RWKAR SH7750_P4_REG32(SH7750_RWKAR_REGOFS) +#define SH7750_RWKAR_A7 SH7750_A7_REG32(SH7750_RWKAR_REGOFS) +#define SH7750_RWKAR_ENB 0x80 /* Day-of-week Alarm Enable */ + +#define SH7750_RWKAR_SUN 0 /* Sunday */ +#define SH7750_RWKAR_MON 1 /* Monday */ +#define SH7750_RWKAR_TUE 2 /* Tuesday */ +#define SH7750_RWKAR_WED 3 /* Wednesday */ +#define SH7750_RWKAR_THU 4 /* Thursday */ +#define SH7750_RWKAR_FRI 5 /* Friday */ +#define SH7750_RWKAR_SAT 6 /* Saturday */ + +/* Day Alarm Register (byte, BCD-coded) - RDAYAR */ +#define SH7750_RDAYAR_REGOFS 0xC80030 /* offset */ +#define SH7750_RDAYAR SH7750_P4_REG32(SH7750_RDAYAR_REGOFS) +#define SH7750_RDAYAR_A7 SH7750_A7_REG32(SH7750_RDAYAR_REGOFS) +#define SH7750_RDAYAR_ENB 0x80 /* Day Alarm Enable */ + +/* Month Counter Register (byte, BCD-coded) - RMONAR */ +#define SH7750_RMONAR_REGOFS 0xC80034 /* offset */ +#define SH7750_RMONAR SH7750_P4_REG32(SH7750_RMONAR_REGOFS) +#define SH7750_RMONAR_A7 SH7750_A7_REG32(SH7750_RMONAR_REGOFS) +#define SH7750_RMONAR_ENB 0x80 /* Month Alarm Enable */ + +/* RTC Control Register 1 (byte) - RCR1 */ +#define SH7750_RCR1_REGOFS 0xC80038 /* offset */ +#define SH7750_RCR1 SH7750_P4_REG32(SH7750_RCR1_REGOFS) +#define SH7750_RCR1_A7 SH7750_A7_REG32(SH7750_RCR1_REGOFS) +#define SH7750_RCR1_CF 0x80 /* Carry Flag */ +#define SH7750_RCR1_CIE 0x10 /* Carry Interrupt Enable */ +#define SH7750_RCR1_AIE 0x08 /* Alarm Interrupt Enable */ +#define SH7750_RCR1_AF 0x01 /* Alarm Flag */ + +/* RTC Control Register 2 (byte) - RCR2 */ +#define SH7750_RCR2_REGOFS 0xC8003C /* offset */ +#define SH7750_RCR2 SH7750_P4_REG32(SH7750_RCR2_REGOFS) +#define SH7750_RCR2_A7 SH7750_A7_REG32(SH7750_RCR2_REGOFS) +#define SH7750_RCR2_PEF 0x80 /* Periodic Interrupt Flag */ +#define SH7750_RCR2_PES 0x70 /* Periodic Interrupt Enable: */ +#define SH7750_RCR2_PES_DIS 0x00 /* Periodic Interrupt Disabled */ +#define SH7750_RCR2_PES_DIV256 0x10 /* Generated at 1/256 sec interval */ +#define SH7750_RCR2_PES_DIV64 0x20 /* Generated at 1/64 sec interval */ +#define SH7750_RCR2_PES_DIV16 0x30 /* Generated at 1/16 sec interval */ +#define SH7750_RCR2_PES_DIV4 0x40 /* Generated at 1/4 sec interval */ +#define SH7750_RCR2_PES_DIV2 0x50 /* Generated at 1/2 sec interval */ +#define SH7750_RCR2_PES_x1 0x60 /* Generated at 1 sec interval */ +#define SH7750_RCR2_PES_x2 0x70 /* Generated at 2 sec interval */ +#define SH7750_RCR2_RTCEN 0x08 /* RTC Crystal Oscillator is Operated */ +#define SH7750_RCR2_ADJ 0x04 /* 30-Second Adjastment */ +#define SH7750_RCR2_RESET 0x02 /* Frequency divider circuits are reset */ +#define SH7750_RCR2_START 0x01 /* 0 - sec, min, hr, day-of-week, month, + year counters are stopped + 1 - sec, min, hr, day-of-week, month, + year counters operate normally */ + + +/* + * Timer Unit (TMU) + */ +/* Timer Output Control Register (byte) - TOCR */ +#define SH7750_TOCR_REGOFS 0xD80000 /* offset */ +#define SH7750_TOCR SH7750_P4_REG32(SH7750_TOCR_REGOFS) +#define SH7750_TOCR_A7 SH7750_A7_REG32(SH7750_TOCR_REGOFS) +#define SH7750_TOCR_TCOE 0x01 /* Timer Clock Pin Control: + 0 - TCLK is used as external clock + input or input capture control + 1 - TCLK is used as on-chip RTC + output clock pin */ + +/* Timer Start Register (byte) - TSTR */ +#define SH7750_TSTR_REGOFS 0xD80004 /* offset */ +#define SH7750_TSTR SH7750_P4_REG32(SH7750_TSTR_REGOFS) +#define SH7750_TSTR_A7 SH7750_A7_REG32(SH7750_TSTR_REGOFS) +#define SH7750_TSTR_STR2 0x04 /* TCNT2 performs count operations */ +#define SH7750_TSTR_STR1 0x02 /* TCNT1 performs count operations */ +#define SH7750_TSTR_STR0 0x01 /* TCNT0 performs count operations */ +#define SH7750_TSTR_STR(n) (1 << (n)) + +/* Timer Constant Register - TCOR0, TCOR1, TCOR2 */ +#define SH7750_TCOR_REGOFS(n) (0xD80008 + ((n)*12)) /* offset */ +#define SH7750_TCOR(n) SH7750_P4_REG32(SH7750_TCOR_REGOFS(n)) +#define SH7750_TCOR_A7(n) SH7750_A7_REG32(SH7750_TCOR_REGOFS(n)) +#define SH7750_TCOR0 SH7750_TCOR(0) +#define SH7750_TCOR1 SH7750_TCOR(1) +#define SH7750_TCOR2 SH7750_TCOR(2) +#define SH7750_TCOR0_A7 SH7750_TCOR_A7(0) +#define SH7750_TCOR1_A7 SH7750_TCOR_A7(1) +#define SH7750_TCOR2_A7 SH7750_TCOR_A7(2) + +/* Timer Counter Register - TCNT0, TCNT1, TCNT2 */ +#define SH7750_TCNT_REGOFS(n) (0xD8000C + ((n)*12)) /* offset */ +#define SH7750_TCNT(n) SH7750_P4_REG32(SH7750_TCNT_REGOFS(n)) +#define SH7750_TCNT_A7(n) SH7750_A7_REG32(SH7750_TCNT_REGOFS(n)) +#define SH7750_TCNT0 SH7750_TCNT(0) +#define SH7750_TCNT1 SH7750_TCNT(1) +#define SH7750_TCNT2 SH7750_TCNT(2) +#define SH7750_TCNT0_A7 SH7750_TCNT_A7(0) +#define SH7750_TCNT1_A7 SH7750_TCNT_A7(1) +#define SH7750_TCNT2_A7 SH7750_TCNT_A7(2) + +/* Timer Control Register (half) - TCR0, TCR1, TCR2 */ +#define SH7750_TCR_REGOFS(n) (0xD80010 + ((n)*12)) /* offset */ +#define SH7750_TCR(n) SH7750_P4_REG32(SH7750_TCR_REGOFS(n)) +#define SH7750_TCR_A7(n) SH7750_A7_REG32(SH7750_TCR_REGOFS(n)) +#define SH7750_TCR0 SH7750_TCR(0) +#define SH7750_TCR1 SH7750_TCR(1) +#define SH7750_TCR2 SH7750_TCR(2) +#define SH7750_TCR0_A7 SH7750_TCR_A7(0) +#define SH7750_TCR1_A7 SH7750_TCR_A7(1) +#define SH7750_TCR2_A7 SH7750_TCR_A7(2) + +#define SH7750_TCR2_ICPF 0x200 /* Input Capture Interrupt Flag + (1 - input capture has occured) */ +#define SH7750_TCR_UNF 0x100 /* Underflow flag */ +#define SH7750_TCR2_ICPE 0x0C0 /* Input Capture Control: */ +#define SH7750_TCR2_ICPE_DIS 0x000 /* Input Capture function is not used */ +#define SH7750_TCR2_ICPE_NOINT 0x080 /* Input Capture function is used, but + input capture interrupt is not + enabled */ +#define SH7750_TCR2_ICPE_INT 0x0C0 /* Input Capture function is used, + input capture interrupt enabled */ +#define SH7750_TCR_UNIE 0x020 /* Underflow Interrupt Control + (1 - underflow interrupt enabled) */ +#define SH7750_TCR_CKEG 0x018 /* Clock Edge selection: */ +#define SH7750_TCR_CKEG_RAISE 0x000 /* Count/capture on rising edge */ +#define SH7750_TCR_CKEG_FALL 0x008 /* Count/capture on falling edge */ +#define SH7750_TCR_CKEG_BOTH 0x018 /* Count/capture on both rising and + falling edges */ +#define SH7750_TCR_TPSC 0x007 /* Timer prescaler */ +#define SH7750_TCR_TPSC_DIV4 0x000 /* Counts on peripheral clock/4 */ +#define SH7750_TCR_TPSC_DIV16 0x001 /* Counts on peripheral clock/16 */ +#define SH7750_TCR_TPSC_DIV64 0x002 /* Counts on peripheral clock/64 */ +#define SH7750_TCR_TPSC_DIV256 0x003 /* Counts on peripheral clock/256 */ +#define SH7750_TCR_TPSC_DIV1024 0x004 /* Counts on peripheral clock/1024 */ +#define SH7750_TCR_TPSC_RTC 0x006 /* Counts on on-chip RTC output clk */ +#define SH7750_TCR_TPSC_EXT 0x007 /* Counts on external clock */ + +/* Input Capture Register (read-only) - TCPR2 */ +#define SH7750_TCPR2_REGOFS 0xD8002C /* offset */ +#define SH7750_TCPR2 SH7750_P4_REG32(SH7750_TCPR2_REGOFS) +#define SH7750_TCPR2_A7 SH7750_A7_REG32(SH7750_TCPR2_REGOFS) + +/* + * Bus State Controller - BSC + */ +/* Bus Control Register 1 - BCR1 */ +#define SH7750_BCR1_REGOFS 0x800000 /* offset */ +#define SH7750_BCR1 SH7750_P4_REG32(SH7750_BCR1_REGOFS) +#define SH7750_BCR1_A7 SH7750_A7_REG32(SH7750_BCR1_REGOFS) +#define SH7750_BCR1_ENDIAN 0x80000000 /* Endianness (1 - little endian) */ +#define SH7750_BCR1_MASTER 0x40000000 /* Master/Slave mode (1-master) */ +#define SH7750_BCR1_A0MPX 0x20000000 /* Area 0 Memory Type (0-SRAM,1-MPX) */ +#define SH7750_BCR1_IPUP 0x02000000 /* Input Pin Pull-up Control: + 0 - pull-up resistor is on for + control input pins + 1 - pull-up resistor is off */ +#define SH7750_BCR1_OPUP 0x01000000 /* Output Pin Pull-up Control: + 0 - pull-up resistor is on for + control output pins + 1 - pull-up resistor is off */ +#define SH7750_BCR1_A1MBC 0x00200000 /* Area 1 SRAM Byte Control Mode: + 0 - Area 1 SRAM is set to + normal mode + 1 - Area 1 SRAM is set to byte + control mode */ +#define SH7750_BCR1_A4MBC 0x00100000 /* Area 4 SRAM Byte Control Mode: + 0 - Area 4 SRAM is set to + normal mode + 1 - Area 4 SRAM is set to byte + control mode */ +#define SH7750_BCR1_BREQEN 0x00080000 /* BREQ Enable: + 0 - External requests are not + accepted + 1 - External requests are + accepted */ +#define SH7750_BCR1_PSHR 0x00040000 /* Partial Sharing Bit: + 0 - Master Mode + 1 - Partial-sharing Mode */ +#define SH7750_BCR1_MEMMPX 0x00020000 /* Area 1 to 6 MPX Interface: + 0 - SRAM/burst ROM interface + 1 - MPX interface */ +#define SH7750_BCR1_HIZMEM 0x00008000 /* High Impendance Control. Specifies + the state of A[25:0], BS\, CSn\, + RD/WR\, CE2A\, CE2B\ in standby + mode and when bus is released: + 0 - signals go to High-Z mode + 1 - signals driven */ +#define SH7750_BCR1_HIZCNT 0x00004000 /* High Impendance Control. Specifies + the state of the RAS\, RAS2\, WEn\, + CASn\, DQMn, RD\, CASS\, FRAME\, + RD2\ signals in standby mode and + when bus is released: + 0 - signals go to High-Z mode + 1 - signals driven */ +#define SH7750_BCR1_A0BST 0x00003800 /* Area 0 Burst ROM Control */ +#define SH7750_BCR1_A0BST_SRAM 0x0000 /* Area 0 accessed as SRAM i/f */ +#define SH7750_BCR1_A0BST_ROM4 0x0800 /* Area 0 accessed as burst ROM + interface, 4 cosequtive access */ +#define SH7750_BCR1_A0BST_ROM8 0x1000 /* Area 0 accessed as burst ROM + interface, 8 cosequtive access */ +#define SH7750_BCR1_A0BST_ROM16 0x1800 /* Area 0 accessed as burst ROM + interface, 16 cosequtive access */ +#define SH7750_BCR1_A0BST_ROM32 0x2000 /* Area 0 accessed as burst ROM + interface, 32 cosequtive access */ + +#define SH7750_BCR1_A5BST 0x00000700 /* Area 5 Burst ROM Control */ +#define SH7750_BCR1_A5BST_SRAM 0x0000 /* Area 5 accessed as SRAM i/f */ +#define SH7750_BCR1_A5BST_ROM4 0x0100 /* Area 5 accessed as burst ROM + interface, 4 cosequtive access */ +#define SH7750_BCR1_A5BST_ROM8 0x0200 /* Area 5 accessed as burst ROM + interface, 8 cosequtive access */ +#define SH7750_BCR1_A5BST_ROM16 0x0300 /* Area 5 accessed as burst ROM + interface, 16 cosequtive access */ +#define SH7750_BCR1_A5BST_ROM32 0x0400 /* Area 5 accessed as burst ROM + interface, 32 cosequtive access */ + +#define SH7750_BCR1_A6BST 0x000000E0 /* Area 6 Burst ROM Control */ +#define SH7750_BCR1_A6BST_SRAM 0x0000 /* Area 6 accessed as SRAM i/f */ +#define SH7750_BCR1_A6BST_ROM4 0x0020 /* Area 6 accessed as burst ROM + interface, 4 cosequtive access */ +#define SH7750_BCR1_A6BST_ROM8 0x0040 /* Area 6 accessed as burst ROM + interface, 8 cosequtive access */ +#define SH7750_BCR1_A6BST_ROM16 0x0060 /* Area 6 accessed as burst ROM + interface, 16 cosequtive access */ +#define SH7750_BCR1_A6BST_ROM32 0x0080 /* Area 6 accessed as burst ROM + interface, 32 cosequtive access */ + +#define SH7750_BCR1_DRAMTP 0x001C /* Area 2 and 3 Memory Type */ +#define SH7750_BCR1_DRAMTP_2SRAM_3SRAM 0x0000 /* Area 2 and 3 are SRAM or MPX + interface. */ +#define SH7750_BCR1_DRAMTP_2SRAM_3SDRAM 0x0008 /* Area 2 - SRAM/MPX, Area 3 - + synchronous DRAM */ +#define SH7750_BCR1_DRAMTP_2SDRAM_3SDRAM 0x000C /* Area 2 and 3 are synchronous + DRAM interface */ +#define SH7750_BCR1_DRAMTP_2SRAM_3DRAM 0x0010 /* Area 2 - SRAM/MPX, Area 3 - + DRAM interface */ +#define SH7750_BCR1_DRAMTP_2DRAM_3DRAM 0x0014 /* Area 2 and 3 are DRAM + interface */ + +#define SH7750_BCR1_A56PCM 0x00000001 /* Area 5 and 6 Bus Type: + 0 - SRAM interface + 1 - PCMCIA interface */ + +/* Bus Control Register 2 (half) - BCR2 */ +#define SH7750_BCR2_REGOFS 0x800004 /* offset */ +#define SH7750_BCR2 SH7750_P4_REG32(SH7750_BCR2_REGOFS) +#define SH7750_BCR2_A7 SH7750_A7_REG32(SH7750_BCR2_REGOFS) + +#define SH7750_BCR2_A0SZ 0xC000 /* Area 0 Bus Width */ +#define SH7750_BCR2_A0SZ_S 14 +#define SH7750_BCR2_A6SZ 0x3000 /* Area 6 Bus Width */ +#define SH7750_BCR2_A6SZ_S 12 +#define SH7750_BCR2_A5SZ 0x0C00 /* Area 5 Bus Width */ +#define SH7750_BCR2_A5SZ_S 10 +#define SH7750_BCR2_A4SZ 0x0300 /* Area 4 Bus Width */ +#define SH7750_BCR2_A4SZ_S 8 +#define SH7750_BCR2_A3SZ 0x00C0 /* Area 3 Bus Width */ +#define SH7750_BCR2_A3SZ_S 6 +#define SH7750_BCR2_A2SZ 0x0030 /* Area 2 Bus Width */ +#define SH7750_BCR2_A2SZ_S 4 +#define SH7750_BCR2_A1SZ 0x000C /* Area 1 Bus Width */ +#define SH7750_BCR2_A1SZ_S 2 +#define SH7750_BCR2_SZ_64 0 /* 64 bits */ +#define SH7750_BCR2_SZ_8 1 /* 8 bits */ +#define SH7750_BCR2_SZ_16 2 /* 16 bits */ +#define SH7750_BCR2_SZ_32 3 /* 32 bits */ +#define SH7750_BCR2_PORTEN 0x0001 /* Port Function Enable : + 0 - D51-D32 are not used as a port + 1 - D51-D32 are used as a port */ + +/* Wait Control Register 1 - WCR1 */ +#define SH7750_WCR1_REGOFS 0x800008 /* offset */ +#define SH7750_WCR1 SH7750_P4_REG32(SH7750_WCR1_REGOFS) +#define SH7750_WCR1_A7 SH7750_A7_REG32(SH7750_WCR1_REGOFS) +#define SH7750_WCR1_DMAIW 0x70000000 /* DACK Device Inter-Cycle Idle + specification */ +#define SH7750_WCR1_DMAIW_S 28 +#define SH7750_WCR1_A6IW 0x07000000 /* Area 6 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A6IW_S 24 +#define SH7750_WCR1_A5IW 0x00700000 /* Area 5 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A5IW_S 20 +#define SH7750_WCR1_A4IW 0x00070000 /* Area 4 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A4IW_S 16 +#define SH7750_WCR1_A3IW 0x00007000 /* Area 3 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A3IW_S 12 +#define SH7750_WCR1_A2IW 0x00000700 /* Area 2 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A2IW_S 8 +#define SH7750_WCR1_A1IW 0x00000070 /* Area 1 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A1IW_S 4 +#define SH7750_WCR1_A0IW 0x00000007 /* Area 0 Inter-Cycle Idle spec. */ +#define SH7750_WCR1_A0IW_S 0 + +/* Wait Control Register 2 - WCR2 */ +#define SH7750_WCR2_REGOFS 0x80000C /* offset */ +#define SH7750_WCR2 SH7750_P4_REG32(SH7750_WCR2_REGOFS) +#define SH7750_WCR2_A7 SH7750_A7_REG32(SH7750_WCR2_REGOFS) + +#define SH7750_WCR2_A6W 0xE0000000 /* Area 6 Wait Control */ +#define SH7750_WCR2_A6W_S 29 +#define SH7750_WCR2_A6B 0x1C000000 /* Area 6 Burst Pitch */ +#define SH7750_WCR2_A6B_S 26 +#define SH7750_WCR2_A5W 0x03800000 /* Area 5 Wait Control */ +#define SH7750_WCR2_A5W_S 23 +#define SH7750_WCR2_A5B 0x00700000 /* Area 5 Burst Pitch */ +#define SH7750_WCR2_A5B_S 20 +#define SH7750_WCR2_A4W 0x000E0000 /* Area 4 Wait Control */ +#define SH7750_WCR2_A4W_S 17 +#define SH7750_WCR2_A3W 0x0000E000 /* Area 3 Wait Control */ +#define SH7750_WCR2_A3W_S 13 +#define SH7750_WCR2_A2W 0x00000E00 /* Area 2 Wait Control */ +#define SH7750_WCR2_A2W_S 9 +#define SH7750_WCR2_A1W 0x000001C0 /* Area 1 Wait Control */ +#define SH7750_WCR2_A1W_S 6 +#define SH7750_WCR2_A0W 0x00000038 /* Area 0 Wait Control */ +#define SH7750_WCR2_A0W_S 3 +#define SH7750_WCR2_A0B 0x00000007 /* Area 0 Burst Pitch */ +#define SH7750_WCR2_A0B_S 0 + +#define SH7750_WCR2_WS0 0 /* 0 wait states inserted */ +#define SH7750_WCR2_WS1 1 /* 1 wait states inserted */ +#define SH7750_WCR2_WS2 2 /* 2 wait states inserted */ +#define SH7750_WCR2_WS3 3 /* 3 wait states inserted */ +#define SH7750_WCR2_WS6 4 /* 6 wait states inserted */ +#define SH7750_WCR2_WS9 5 /* 9 wait states inserted */ +#define SH7750_WCR2_WS12 6 /* 12 wait states inserted */ +#define SH7750_WCR2_WS15 7 /* 15 wait states inserted */ + +#define SH7750_WCR2_BPWS0 0 /* 0 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS1 1 /* 1 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS2 2 /* 2 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS3 3 /* 3 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS4 4 /* 4 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS5 5 /* 5 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS6 6 /* 6 wait states inserted from 2nd access */ +#define SH7750_WCR2_BPWS7 7 /* 7 wait states inserted from 2nd access */ + +/* DRAM CAS\ Assertion Delay (area 3,2) */ +#define SH7750_WCR2_DRAM_CAS_ASW1 0 /* 1 cycle */ +#define SH7750_WCR2_DRAM_CAS_ASW2 1 /* 2 cycles */ +#define SH7750_WCR2_DRAM_CAS_ASW3 2 /* 3 cycles */ +#define SH7750_WCR2_DRAM_CAS_ASW4 3 /* 4 cycles */ +#define SH7750_WCR2_DRAM_CAS_ASW7 4 /* 7 cycles */ +#define SH7750_WCR2_DRAM_CAS_ASW10 5 /* 10 cycles */ +#define SH7750_WCR2_DRAM_CAS_ASW13 6 /* 13 cycles */ +#define SH7750_WCR2_DRAM_CAS_ASW16 7 /* 16 cycles */ + +/* SDRAM CAS\ Latency Cycles */ +#define SH7750_WCR2_SDRAM_CAS_LAT1 1 /* 1 cycle */ +#define SH7750_WCR2_SDRAM_CAS_LAT2 2 /* 2 cycles */ +#define SH7750_WCR2_SDRAM_CAS_LAT3 3 /* 3 cycles */ +#define SH7750_WCR2_SDRAM_CAS_LAT4 4 /* 4 cycles */ +#define SH7750_WCR2_SDRAM_CAS_LAT5 5 /* 5 cycles */ + +/* Wait Control Register 3 - WCR3 */ +#define SH7750_WCR3_REGOFS 0x800010 /* offset */ +#define SH7750_WCR3 SH7750_P4_REG32(SH7750_WCR3_REGOFS) +#define SH7750_WCR3_A7 SH7750_A7_REG32(SH7750_WCR3_REGOFS) + +#define SH7750_WCR3_A6S 0x04000000 /* Area 6 Write Strobe Setup time */ +#define SH7750_WCR3_A6H 0x03000000 /* Area 6 Data Hold Time */ +#define SH7750_WCR3_A6H_S 24 +#define SH7750_WCR3_A5S 0x00400000 /* Area 5 Write Strobe Setup time */ +#define SH7750_WCR3_A5H 0x00300000 /* Area 5 Data Hold Time */ +#define SH7750_WCR3_A5H_S 20 +#define SH7750_WCR3_A4S 0x00040000 /* Area 4 Write Strobe Setup time */ +#define SH7750_WCR3_A4H 0x00030000 /* Area 4 Data Hold Time */ +#define SH7750_WCR3_A4H_S 16 +#define SH7750_WCR3_A3S 0x00004000 /* Area 3 Write Strobe Setup time */ +#define SH7750_WCR3_A3H 0x00003000 /* Area 3 Data Hold Time */ +#define SH7750_WCR3_A3H_S 12 +#define SH7750_WCR3_A2S 0x00000400 /* Area 2 Write Strobe Setup time */ +#define SH7750_WCR3_A2H 0x00000300 /* Area 2 Data Hold Time */ +#define SH7750_WCR3_A2H_S 8 +#define SH7750_WCR3_A1S 0x00000040 /* Area 1 Write Strobe Setup time */ +#define SH7750_WCR3_A1H 0x00000030 /* Area 1 Data Hold Time */ +#define SH7750_WCR3_A1H_S 4 +#define SH7750_WCR3_A0S 0x00000004 /* Area 0 Write Strobe Setup time */ +#define SH7750_WCR3_A0H 0x00000003 /* Area 0 Data Hold Time */ +#define SH7750_WCR3_A0H_S 0 + +#define SH7750_WCR3_DHWS_0 0 /* 0 wait states data hold time */ +#define SH7750_WCR3_DHWS_1 1 /* 1 wait states data hold time */ +#define SH7750_WCR3_DHWS_2 2 /* 2 wait states data hold time */ +#define SH7750_WCR3_DHWS_3 3 /* 3 wait states data hold time */ + +#define SH7750_MCR_REGOFS 0x800014 /* offset */ +#define SH7750_MCR SH7750_P4_REG32(SH7750_MCR_REGOFS) +#define SH7750_MCR_A7 SH7750_A7_REG32(SH7750_MCR_REGOFS) + +#define SH7750_MCR_RASD 0x80000000 /* RAS Down mode */ +#define SH7750_MCR_MRSET 0x40000000 /* SDRAM Mode Register Set */ +#define SH7750_MCR_PALL 0x00000000 /* SDRAM Precharge All cmd. Mode */ +#define SH7750_MCR_TRC 0x38000000 /* RAS Precharge Time at End of + Refresh: */ +#define SH7750_MCR_TRC_0 0x00000000 /* 0 */ +#define SH7750_MCR_TRC_3 0x08000000 /* 3 */ +#define SH7750_MCR_TRC_6 0x10000000 /* 6 */ +#define SH7750_MCR_TRC_9 0x18000000 /* 9 */ +#define SH7750_MCR_TRC_12 0x20000000 /* 12 */ +#define SH7750_MCR_TRC_15 0x28000000 /* 15 */ +#define SH7750_MCR_TRC_18 0x30000000 /* 18 */ +#define SH7750_MCR_TRC_21 0x38000000 /* 21 */ + +#define SH7750_MCR_TCAS 0x00800000 /* CAS Negation Period */ +#define SH7750_MCR_TCAS_1 0x00000000 /* 1 */ +#define SH7750_MCR_TCAS_2 0x00800000 /* 2 */ + +#define SH7750_MCR_TPC 0x00380000 /* DRAM: RAS Precharge Period + SDRAM: minimum number of cycles + until the next bank active cmd + is output after precharging */ +#define SH7750_MCR_TPC_S 19 +#define SH7750_MCR_TPC_SDRAM_1 0x00000000 /* 1 cycle */ +#define SH7750_MCR_TPC_SDRAM_2 0x00080000 /* 2 cycles */ +#define SH7750_MCR_TPC_SDRAM_3 0x00100000 /* 3 cycles */ +#define SH7750_MCR_TPC_SDRAM_4 0x00180000 /* 4 cycles */ +#define SH7750_MCR_TPC_SDRAM_5 0x00200000 /* 5 cycles */ +#define SH7750_MCR_TPC_SDRAM_6 0x00280000 /* 6 cycles */ +#define SH7750_MCR_TPC_SDRAM_7 0x00300000 /* 7 cycles */ +#define SH7750_MCR_TPC_SDRAM_8 0x00380000 /* 8 cycles */ + +#define SH7750_MCR_RCD 0x00030000 /* DRAM: RAS-CAS Assertion Delay time + SDRAM: bank active-read/write cmd + delay time */ +#define SH7750_MCR_RCD_DRAM_2 0x00000000 /* DRAM delay 2 clocks */ +#define SH7750_MCR_RCD_DRAM_3 0x00010000 /* DRAM delay 3 clocks */ +#define SH7750_MCR_RCD_DRAM_4 0x00020000 /* DRAM delay 4 clocks */ +#define SH7750_MCR_RCD_DRAM_5 0x00030000 /* DRAM delay 5 clocks */ +#define SH7750_MCR_RCD_SDRAM_2 0x00010000 /* DRAM delay 2 clocks */ +#define SH7750_MCR_RCD_SDRAM_3 0x00020000 /* DRAM delay 3 clocks */ +#define SH7750_MCR_RCD_SDRAM_4 0x00030000 /* DRAM delay 4 clocks */ + +#define SH7750_MCR_TRWL 0x0000E000 /* SDRAM Write Precharge Delay */ +#define SH7750_MCR_TRWL_1 0x00000000 /* 1 */ +#define SH7750_MCR_TRWL_2 0x00002000 /* 2 */ +#define SH7750_MCR_TRWL_3 0x00004000 /* 3 */ +#define SH7750_MCR_TRWL_4 0x00006000 /* 4 */ +#define SH7750_MCR_TRWL_5 0x00008000 /* 5 */ + +#define SH7750_MCR_TRAS 0x00001C00 /* DRAM: CAS-Before-RAS Refresh RAS + asserting period + SDRAM: Command interval after + synchronous DRAM refresh */ +#define SH7750_MCR_TRAS_DRAM_2 0x00000000 /* 2 */ +#define SH7750_MCR_TRAS_DRAM_3 0x00000400 /* 3 */ +#define SH7750_MCR_TRAS_DRAM_4 0x00000800 /* 4 */ +#define SH7750_MCR_TRAS_DRAM_5 0x00000C00 /* 5 */ +#define SH7750_MCR_TRAS_DRAM_6 0x00001000 /* 6 */ +#define SH7750_MCR_TRAS_DRAM_7 0x00001400 /* 7 */ +#define SH7750_MCR_TRAS_DRAM_8 0x00001800 /* 8 */ +#define SH7750_MCR_TRAS_DRAM_9 0x00001C00 /* 9 */ + +#define SH7750_MCR_TRAS_SDRAM_TRC_4 0x00000000 /* 4 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_5 0x00000400 /* 5 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_6 0x00000800 /* 6 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_7 0x00000C00 /* 7 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_8 0x00001000 /* 8 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_9 0x00001400 /* 9 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_10 0x00001800 /* 10 + TRC */ +#define SH7750_MCR_TRAS_SDRAM_TRC_11 0x00001C00 /* 11 + TRC */ + +#define SH7750_MCR_BE 0x00000200 /* Burst Enable */ +#define SH7750_MCR_SZ 0x00000180 /* Memory Data Size */ +#define SH7750_MCR_SZ_64 0x00000000 /* 64 bits */ +#define SH7750_MCR_SZ_16 0x00000100 /* 16 bits */ +#define SH7750_MCR_SZ_32 0x00000180 /* 32 bits */ + +#define SH7750_MCR_AMX 0x00000078 /* Address Multiplexing */ +#define SH7750_MCR_AMX_S 3 +#define SH7750_MCR_AMX_DRAM_8BIT_COL 0x00000000 /* 8-bit column addr */ +#define SH7750_MCR_AMX_DRAM_9BIT_COL 0x00000008 /* 9-bit column addr */ +#define SH7750_MCR_AMX_DRAM_10BIT_COL 0x00000010 /* 10-bit column addr */ +#define SH7750_MCR_AMX_DRAM_11BIT_COL 0x00000018 /* 11-bit column addr */ +#define SH7750_MCR_AMX_DRAM_12BIT_COL 0x00000020 /* 12-bit column addr */ +/* See SH7750 Hardware Manual for SDRAM address multiplexor selection */ + +#define SH7750_MCR_RFSH 0x00000004 /* Refresh Control */ +#define SH7750_MCR_RMODE 0x00000002 /* Refresh Mode: */ +#define SH7750_MCR_RMODE_NORMAL 0x00000000 /* Normal Refresh Mode */ +#define SH7750_MCR_RMODE_SELF 0x00000002 /* Self-Refresh Mode */ +#define SH7750_MCR_RMODE_EDO 0x00000001 /* EDO Mode */ + +/* SDRAM Mode Set address */ +#define SH7750_SDRAM_MODE_A2_BASE 0xFF900000 +#define SH7750_SDRAM_MODE_A3_BASE 0xFF940000 +#define SH7750_SDRAM_MODE_A2_32BIT(x) (SH7750_SDRAM_MODE_A2_BASE + ((x) << 2)) +#define SH7750_SDRAM_MODE_A3_32BIT(x) (SH7750_SDRAM_MODE_A3_BASE + ((x) << 2)) +#define SH7750_SDRAM_MODE_A2_64BIT(x) (SH7750_SDRAM_MODE_A2_BASE + ((x) << 3)) +#define SH7750_SDRAM_MODE_A3_64BIT(x) (SH7750_SDRAM_MODE_A3_BASE + ((x) << 3)) + + +/* PCMCIA Control Register (half) - PCR */ +#define SH7750_PCR_REGOFS 0x800018 /* offset */ +#define SH7750_PCR SH7750_P4_REG32(SH7750_PCR_REGOFS) +#define SH7750_PCR_A7 SH7750_A7_REG32(SH7750_PCR_REGOFS) + +#define SH7750_PCR_A5PCW 0xC000 /* Area 5 PCMCIA Wait - Number of wait + states to be added to the number of + waits specified by WCR2 in a low-speed + PCMCIA wait cycle */ +#define SH7750_PCR_A5PCW_0 0x0000 /* 0 waits inserted */ +#define SH7750_PCR_A5PCW_15 0x4000 /* 15 waits inserted */ +#define SH7750_PCR_A5PCW_30 0x8000 /* 30 waits inserted */ +#define SH7750_PCR_A5PCW_50 0xC000 /* 50 waits inserted */ + +#define SH7750_PCR_A6PCW 0x3000 /* Area 6 PCMCIA Wait - Number of wait + states to be added to the number of + waits specified by WCR2 in a low-speed + PCMCIA wait cycle */ +#define SH7750_PCR_A6PCW_0 0x0000 /* 0 waits inserted */ +#define SH7750_PCR_A6PCW_15 0x1000 /* 15 waits inserted */ +#define SH7750_PCR_A6PCW_30 0x2000 /* 30 waits inserted */ +#define SH7750_PCR_A6PCW_50 0x3000 /* 50 waits inserted */ + +#define SH7750_PCR_A5TED 0x0E00 /* Area 5 Address-OE\/WE\ Assertion Delay, + delay time from address output to + OE\/WE\ assertion on the connected + PCMCIA interface */ +#define SH7750_PCR_A5TED_S 9 +#define SH7750_PCR_A6TED 0x01C0 /* Area 6 Address-OE\/WE\ Assertion Delay */ +#define SH7750_PCR_A6TED_S 6 + +#define SH7750_PCR_TED_0WS 0 /* 0 Waits inserted */ +#define SH7750_PCR_TED_1WS 1 /* 1 Waits inserted */ +#define SH7750_PCR_TED_2WS 2 /* 2 Waits inserted */ +#define SH7750_PCR_TED_3WS 3 /* 3 Waits inserted */ +#define SH7750_PCR_TED_6WS 4 /* 6 Waits inserted */ +#define SH7750_PCR_TED_9WS 5 /* 9 Waits inserted */ +#define SH7750_PCR_TED_12WS 6 /* 12 Waits inserted */ +#define SH7750_PCR_TED_15WS 7 /* 15 Waits inserted */ + +#define SH7750_PCR_A5TEH 0x0038 /* Area 5 OE\/WE\ Negation Address delay, + address hold delay time from OE\/WE\ + negation in a write on the connected + PCMCIA interface */ +#define SH7750_PCR_A5TEH_S 3 + +#define SH7750_PCR_A6TEH 0x0007 /* Area 6 OE\/WE\ Negation Address delay */ +#define SH7750_PCR_A6TEH_S 0 + +#define SH7750_PCR_TEH_0WS 0 /* 0 Waits inserted */ +#define SH7750_PCR_TEH_1WS 1 /* 1 Waits inserted */ +#define SH7750_PCR_TEH_2WS 2 /* 2 Waits inserted */ +#define SH7750_PCR_TEH_3WS 3 /* 3 Waits inserted */ +#define SH7750_PCR_TEH_6WS 4 /* 6 Waits inserted */ +#define SH7750_PCR_TEH_9WS 5 /* 9 Waits inserted */ +#define SH7750_PCR_TEH_12WS 6 /* 12 Waits inserted */ +#define SH7750_PCR_TEH_15WS 7 /* 15 Waits inserted */ + +/* Refresh Timer Control/Status Register (half) - RTSCR */ +#define SH7750_RTCSR_REGOFS 0x80001C /* offset */ +#define SH7750_RTCSR SH7750_P4_REG32(SH7750_RTCSR_REGOFS) +#define SH7750_RTCSR_A7 SH7750_A7_REG32(SH7750_RTCSR_REGOFS) + +#define SH7750_RTCSR_KEY 0xA500 /* RTCSR write key */ +#define SH7750_RTCSR_CMF 0x0080 /* Compare-Match Flag (indicates a + match between the refresh timer + counter and refresh time constant) */ +#define SH7750_RTCSR_CMIE 0x0040 /* Compare-Match Interrupt Enable */ +#define SH7750_RTCSR_CKS 0x0038 /* Refresh Counter Clock Selects */ +#define SH7750_RTCSR_CKS_DIS 0x0000 /* Clock Input Disabled */ +#define SH7750_RTCSR_CKS_CKIO_DIV4 0x0008 /* Bus Clock / 4 */ +#define SH7750_RTCSR_CKS_CKIO_DIV16 0x0010 /* Bus Clock / 16 */ +#define SH7750_RTCSR_CKS_CKIO_DIV64 0x0018 /* Bus Clock / 64 */ +#define SH7750_RTCSR_CKS_CKIO_DIV256 0x0020 /* Bus Clock / 256 */ +#define SH7750_RTCSR_CKS_CKIO_DIV1024 0x0028 /* Bus Clock / 1024 */ +#define SH7750_RTCSR_CKS_CKIO_DIV2048 0x0030 /* Bus Clock / 2048 */ +#define SH7750_RTCSR_CKS_CKIO_DIV4096 0x0038 /* Bus Clock / 4096 */ + +#define SH7750_RTCSR_OVF 0x0004 /* Refresh Count Overflow Flag */ +#define SH7750_RTCSR_OVIE 0x0002 /* Refresh Count Overflow Interrupt + Enable */ +#define SH7750_RTCSR_LMTS 0x0001 /* Refresh Count Overflow Limit Select */ +#define SH7750_RTCSR_LMTS_1024 0x0000 /* Count Limit is 1024 */ +#define SH7750_RTCSR_LMTS_512 0x0001 /* Count Limit is 512 */ + +/* Refresh Timer Counter (half) - RTCNT */ +#define SH7750_RTCNT_REGOFS 0x800020 /* offset */ +#define SH7750_RTCNT SH7750_P4_REG32(SH7750_RTCNT_REGOFS) +#define SH7750_RTCNT_A7 SH7750_A7_REG32(SH7750_RTCNT_REGOFS) + +#define SH7750_RTCNT_KEY 0xA500 /* RTCNT write key */ + +/* Refresh Time Constant Register (half) - RTCOR */ +#define SH7750_RTCOR_REGOFS 0x800024 /* offset */ +#define SH7750_RTCOR SH7750_P4_REG32(SH7750_RTCOR_REGOFS) +#define SH7750_RTCOR_A7 SH7750_A7_REG32(SH7750_RTCOR_REGOFS) + +#define SH7750_RTCOR_KEY 0xA500 /* RTCOR write key */ + +/* Refresh Count Register (half) - RFCR */ +#define SH7750_RFCR_REGOFS 0x800028 /* offset */ +#define SH7750_RFCR SH7750_P4_REG32(SH7750_RFCR_REGOFS) +#define SH7750_RFCR_A7 SH7750_A7_REG32(SH7750_RFCR_REGOFS) + +#define SH7750_RFCR_KEY 0xA400 /* RFCR write key */ + +/* + * Direct Memory Access Controller (DMAC) + */ + +/* DMA Source Address Register - SAR0, SAR1, SAR2, SAR3 */ +#define SH7750_SAR_REGOFS(n) (0xA00000 + ((n)*16)) /* offset */ +#define SH7750_SAR(n) SH7750_P4_REG32(SH7750_SAR_REGOFS(n)) +#define SH7750_SAR_A7(n) SH7750_A7_REG32(SH7750_SAR_REGOFS(n)) +#define SH7750_SAR0 SH7750_SAR(0) +#define SH7750_SAR1 SH7750_SAR(1) +#define SH7750_SAR2 SH7750_SAR(2) +#define SH7750_SAR3 SH7750_SAR(3) +#define SH7750_SAR0_A7 SH7750_SAR_A7(0) +#define SH7750_SAR1_A7 SH7750_SAR_A7(1) +#define SH7750_SAR2_A7 SH7750_SAR_A7(2) +#define SH7750_SAR3_A7 SH7750_SAR_A7(3) + +/* DMA Destination Address Register - DAR0, DAR1, DAR2, DAR3 */ +#define SH7750_DAR_REGOFS(n) (0xA00004 + ((n)*16)) /* offset */ +#define SH7750_DAR(n) SH7750_P4_REG32(SH7750_DAR_REGOFS(n)) +#define SH7750_DAR_A7(n) SH7750_A7_REG32(SH7750_DAR_REGOFS(n)) +#define SH7750_DAR0 SH7750_DAR(0) +#define SH7750_DAR1 SH7750_DAR(1) +#define SH7750_DAR2 SH7750_DAR(2) +#define SH7750_DAR3 SH7750_DAR(3) +#define SH7750_DAR0_A7 SH7750_DAR_A7(0) +#define SH7750_DAR1_A7 SH7750_DAR_A7(1) +#define SH7750_DAR2_A7 SH7750_DAR_A7(2) +#define SH7750_DAR3_A7 SH7750_DAR_A7(3) + +/* DMA Transfer Count Register - DMATCR0, DMATCR1, DMATCR2, DMATCR3 */ +#define SH7750_DMATCR_REGOFS(n) (0xA00008 + ((n)*16)) /* offset */ +#define SH7750_DMATCR(n) SH7750_P4_REG32(SH7750_DMATCR_REGOFS(n)) +#define SH7750_DMATCR_A7(n) SH7750_A7_REG32(SH7750_DMATCR_REGOFS(n)) +#define SH7750_DMATCR0_P4 SH7750_DMATCR(0) +#define SH7750_DMATCR1_P4 SH7750_DMATCR(1) +#define SH7750_DMATCR2_P4 SH7750_DMATCR(2) +#define SH7750_DMATCR3_P4 SH7750_DMATCR(3) +#define SH7750_DMATCR0_A7 SH7750_DMATCR_A7(0) +#define SH7750_DMATCR1_A7 SH7750_DMATCR_A7(1) +#define SH7750_DMATCR2_A7 SH7750_DMATCR_A7(2) +#define SH7750_DMATCR3_A7 SH7750_DMATCR_A7(3) + +/* DMA Channel Control Register - CHCR0, CHCR1, CHCR2, CHCR3 */ +#define SH7750_CHCR_REGOFS(n) (0xA0000C + ((n)*16)) /* offset */ +#define SH7750_CHCR(n) SH7750_P4_REG32(SH7750_CHCR_REGOFS(n)) +#define SH7750_CHCR_A7(n) SH7750_A7_REG32(SH7750_CHCR_REGOFS(n)) +#define SH7750_CHCR0 SH7750_CHCR(0) +#define SH7750_CHCR1 SH7750_CHCR(1) +#define SH7750_CHCR2 SH7750_CHCR(2) +#define SH7750_CHCR3 SH7750_CHCR(3) +#define SH7750_CHCR0_A7 SH7750_CHCR_A7(0) +#define SH7750_CHCR1_A7 SH7750_CHCR_A7(1) +#define SH7750_CHCR2_A7 SH7750_CHCR_A7(2) +#define SH7750_CHCR3_A7 SH7750_CHCR_A7(3) + +#define SH7750_CHCR_SSA 0xE0000000 /* Source Address Space Attribute */ +#define SH7750_CHCR_SSA_PCMCIA 0x00000000 /* Reserved in PCMCIA access */ +#define SH7750_CHCR_SSA_DYNBSZ 0x20000000 /* Dynamic Bus Sizing I/O space */ +#define SH7750_CHCR_SSA_IO8 0x40000000 /* 8-bit I/O space */ +#define SH7750_CHCR_SSA_IO16 0x60000000 /* 16-bit I/O space */ +#define SH7750_CHCR_SSA_CMEM8 0x80000000 /* 8-bit common memory space */ +#define SH7750_CHCR_SSA_CMEM16 0xA0000000 /* 16-bit common memory space */ +#define SH7750_CHCR_SSA_AMEM8 0xC0000000 /* 8-bit attribute memory space */ +#define SH7750_CHCR_SSA_AMEM16 0xE0000000 /* 16-bit attribute memory space */ + +#define SH7750_CHCR_STC 0x10000000 /* Source Address Wait Control Select, + specifies CS5 or CS6 space wait + control for PCMCIA access */ + +#define SH7750_CHCR_DSA 0x0E000000 /* Source Address Space Attribute */ +#define SH7750_CHCR_DSA_PCMCIA 0x00000000 /* Reserved in PCMCIA access */ +#define SH7750_CHCR_DSA_DYNBSZ 0x02000000 /* Dynamic Bus Sizing I/O space */ +#define SH7750_CHCR_DSA_IO8 0x04000000 /* 8-bit I/O space */ +#define SH7750_CHCR_DSA_IO16 0x06000000 /* 16-bit I/O space */ +#define SH7750_CHCR_DSA_CMEM8 0x08000000 /* 8-bit common memory space */ +#define SH7750_CHCR_DSA_CMEM16 0x0A000000 /* 16-bit common memory space */ +#define SH7750_CHCR_DSA_AMEM8 0x0C000000 /* 8-bit attribute memory space */ +#define SH7750_CHCR_DSA_AMEM16 0x0E000000 /* 16-bit attribute memory space */ + +#define SH7750_CHCR_DTC 0x01000000 /* Destination Address Wait Control + Select, specifies CS5 or CS6 + space wait control for PCMCIA + access */ + +#define SH7750_CHCR_DS 0x00080000 /* DREQ\ Select : */ +#define SH7750_CHCR_DS_LOWLVL 0x00000000 /* Low Level Detection */ +#define SH7750_CHCR_DS_FALL 0x00080000 /* Falling Edge Detection */ + +#define SH7750_CHCR_RL 0x00040000 /* Request Check Level: */ +#define SH7750_CHCR_RL_ACTH 0x00000000 /* DRAK is an active high out */ +#define SH7750_CHCR_RL_ACTL 0x00040000 /* DRAK is an active low out */ + +#define SH7750_CHCR_AM 0x00020000 /* Acknowledge Mode: */ +#define SH7750_CHCR_AM_RD 0x00000000 /* DACK is output in read cycle */ +#define SH7750_CHCR_AM_WR 0x00020000 /* DACK is output in write cycle */ + +#define SH7750_CHCR_AL 0x00010000 /* Acknowledge Level: */ +#define SH7750_CHCR_AL_ACTH 0x00000000 /* DACK is an active high out */ +#define SH7750_CHCR_AL_ACTL 0x00010000 /* DACK is an active low out */ + +#define SH7750_CHCR_DM 0x0000C000 /* Destination Address Mode: */ +#define SH7750_CHCR_DM_FIX 0x00000000 /* Destination Addr Fixed */ +#define SH7750_CHCR_DM_INC 0x00004000 /* Destination Addr Incremented */ +#define SH7750_CHCR_DM_DEC 0x00008000 /* Destination Addr Decremented */ + +#define SH7750_CHCR_SM 0x00003000 /* Source Address Mode: */ +#define SH7750_CHCR_SM_FIX 0x00000000 /* Source Addr Fixed */ +#define SH7750_CHCR_SM_INC 0x00001000 /* Source Addr Incremented */ +#define SH7750_CHCR_SM_DEC 0x00002000 /* Source Addr Decremented */ + +#define SH7750_CHCR_RS 0x00000F00 /* Request Source Select: */ +#define SH7750_CHCR_RS_ER_DA_EA_TO_EA 0x000 /* External Request, Dual Address + Mode (External Addr Space-> + External Addr Space) */ +#define SH7750_CHCR_RS_ER_SA_EA_TO_ED 0x200 /* External Request, Single + Address Mode (External Addr + Space -> External Device) */ +#define SH7750_CHCR_RS_ER_SA_ED_TO_EA 0x300 /* External Request, Single + Address Mode, (External + Device -> External Addr + Space) */ +#define SH7750_CHCR_RS_AR_EA_TO_EA 0x400 /* Auto-Request (External Addr + Space -> External Addr Space) */ + +#define SH7750_CHCR_RS_AR_EA_TO_OCP 0x500 /* Auto-Request (External Addr + Space -> On-chip Peripheral + Module) */ +#define SH7750_CHCR_RS_AR_OCP_TO_EA 0x600 /* Auto-Request (On-chip + Peripheral Module -> + External Addr Space */ +#define SH7750_CHCR_RS_SCITX_EA_TO_SC 0x800 /* SCI Transmit-Data-Empty intr + transfer request (external + address space -> SCTDR1) */ +#define SH7750_CHCR_RS_SCIRX_SC_TO_EA 0x900 /* SCI Receive-Data-Full intr + transfer request (SCRDR1 -> + External Addr Space) */ +#define SH7750_CHCR_RS_SCIFTX_EA_TO_SC 0xA00 /* SCIF Transmit-Data-Empty intr + transfer request (external + address space -> SCFTDR1) */ +#define SH7750_CHCR_RS_SCIFRX_SC_TO_EA 0xB00 /* SCIF Receive-Data-Full intr + transfer request (SCFRDR2 -> + External Addr Space) */ +#define SH7750_CHCR_RS_TMU2_EA_TO_EA 0xC00 /* TMU Channel 2 (input capture + interrupt), (external address + space -> external address + space) */ +#define SH7750_CHCR_RS_TMU2_EA_TO_OCP 0xD00 /* TMU Channel 2 (input capture + interrupt), (external address + space -> on-chip peripheral + module) */ +#define SH7750_CHCR_RS_TMU2_OCP_TO_EA 0xE00 /* TMU Channel 2 (input capture + interrupt), (on-chip + peripheral module -> external + address space) */ + +#define SH7750_CHCR_TM 0x00000080 /* Transmit mode: */ +#define SH7750_CHCR_TM_CSTEAL 0x00000000 /* Cycle Steal Mode */ +#define SH7750_CHCR_TM_BURST 0x00000080 /* Burst Mode */ + +#define SH7750_CHCR_TS 0x00000070 /* Transmit Size: */ +#define SH7750_CHCR_TS_QUAD 0x00000000 /* Quadword Size (64 bits) */ +#define SH7750_CHCR_TS_BYTE 0x00000010 /* Byte Size (8 bit) */ +#define SH7750_CHCR_TS_WORD 0x00000020 /* Word Size (16 bit) */ +#define SH7750_CHCR_TS_LONG 0x00000030 /* Longword Size (32 bit) */ +#define SH7750_CHCR_TS_BLOCK 0x00000040 /* 32-byte block transfer */ + +#define SH7750_CHCR_IE 0x00000004 /* Interrupt Enable */ +#define SH7750_CHCR_TE 0x00000002 /* Transfer End */ +#define SH7750_CHCR_DE 0x00000001 /* DMAC Enable */ + +/* DMA Operation Register - DMAOR */ +#define SH7750_DMAOR_REGOFS 0xA00040 /* offset */ +#define SH7750_DMAOR SH7750_P4_REG32(SH7750_DMAOR_REGOFS) +#define SH7750_DMAOR_A7 SH7750_A7_REG32(SH7750_DMAOR_REGOFS) + +#define SH7750_DMAOR_DDT 0x00008000 /* On-Demand Data Transfer Mode */ + +#define SH7750_DMAOR_PR 0x00000300 /* Priority Mode: */ +#define SH7750_DMAOR_PR_0123 0x00000000 /* CH0 > CH1 > CH2 > CH3 */ +#define SH7750_DMAOR_PR_0231 0x00000100 /* CH0 > CH2 > CH3 > CH1 */ +#define SH7750_DMAOR_PR_2013 0x00000200 /* CH2 > CH0 > CH1 > CH3 */ +#define SH7750_DMAOR_PR_RR 0x00000300 /* Round-robin mode */ + +#define SH7750_DMAOR_COD 0x00000010 /* Check Overrun for DREQ\ */ +#define SH7750_DMAOR_AE 0x00000004 /* Address Error flag */ +#define SH7750_DMAOR_NMIF 0x00000002 /* NMI Flag */ +#define SH7750_DMAOR_DME 0x00000001 /* DMAC Master Enable */ + +/* + * Serial Communication Interface - SCI + * Serial Communication Interface with FIFO - SCIF + */ +/* SCI Receive Data Register (byte, read-only) - SCRDR1, SCFRDR2 */ +#define SH7750_SCRDR_REGOFS(n) ((n) == 1 ? 0xE00014 : 0xE80014) /* offset */ +#define SH7750_SCRDR(n) SH7750_P4_REG32(SH7750_SCRDR_REGOFS(n)) +#define SH7750_SCRDR1 SH7750_SCRDR(1) +#define SH7750_SCRDR2 SH7750_SCRDR(2) +#define SH7750_SCRDR_A7(n) SH7750_A7_REG32(SH7750_SCRDR_REGOFS(n)) +#define SH7750_SCRDR1_A7 SH7750_SCRDR_A7(1) +#define SH7750_SCRDR2_A7 SH7750_SCRDR_A7(2) + +/* SCI Transmit Data Register (byte) - SCTDR1, SCFTDR2 */ +#define SH7750_SCTDR_REGOFS(n) ((n) == 1 ? 0xE0000C : 0xE8000C) /* offset */ +#define SH7750_SCTDR(n) SH7750_P4_REG32(SH7750_SCTDR_REGOFS(n)) +#define SH7750_SCTDR1 SH7750_SCTDR(1) +#define SH7750_SCTDR2 SH7750_SCTDR(2) +#define SH7750_SCTDR_A7(n) SH7750_A7_REG32(SH7750_SCTDR_REGOFS(n)) +#define SH7750_SCTDR1_A7 SH7750_SCTDR_A7(1) +#define SH7750_SCTDR2_A7 SH7750_SCTDR_A7(2) + +/* SCI Serial Mode Register - SCSMR1(byte), SCSMR2(half) */ +#define SH7750_SCSMR_REGOFS(n) ((n) == 1 ? 0xE00000 : 0xE80000) /* offset */ +#define SH7750_SCSMR(n) SH7750_P4_REG32(SH7750_SCSMR_REGOFS(n)) +#define SH7750_SCSMR1 SH7750_SCSMR(1) +#define SH7750_SCSMR2 SH7750_SCSMR(2) +#define SH7750_SCSMR_A7(n) SH7750_A7_REG32(SH7750_SCSMR_REGOFS(n)) +#define SH7750_SCSMR1_A7 SH7750_SCSMR_A7(1) +#define SH7750_SCSMR2_A7 SH7750_SCSMR_A7(2) + +#define SH7750_SCSMR1_CA 0x80 /* Communication Mode (C/A\): */ +#define SH7750_SCSMR1_CA_ASYNC 0x00 /* Asynchronous Mode */ +#define SH7750_SCSMR1_CA_SYNC 0x80 /* Synchronous Mode */ +#define SH7750_SCSMR_CHR 0x40 /* Character Length: */ +#define SH7750_SCSMR_CHR_8 0x00 /* 8-bit data */ +#define SH7750_SCSMR_CHR_7 0x40 /* 7-bit data */ +#define SH7750_SCSMR_PE 0x20 /* Parity Enable */ +#define SH7750_SCSMR_PM 0x10 /* Parity Mode: */ +#define SH7750_SCSMR_PM_EVEN 0x00 /* Even Parity */ +#define SH7750_SCSMR_PM_ODD 0x10 /* Odd Parity */ +#define SH7750_SCSMR_STOP 0x08 /* Stop Bit Length: */ +#define SH7750_SCSMR_STOP_1 0x00 /* 1 stop bit */ +#define SH7750_SCSMR_STOP_2 0x08 /* 2 stop bit */ +#define SH7750_SCSMR1_MP 0x04 /* Multiprocessor Mode */ +#define SH7750_SCSMR_CKS 0x03 /* Clock Select */ +#define SH7750_SCSMR_CKS_S 0 +#define SH7750_SCSMR_CKS_DIV1 0x00 /* Periph clock */ +#define SH7750_SCSMR_CKS_DIV4 0x01 /* Periph clock / 4 */ +#define SH7750_SCSMR_CKS_DIV16 0x02 /* Periph clock / 16 */ +#define SH7750_SCSMR_CKS_DIV64 0x03 /* Periph clock / 64 */ + +/* SCI Serial Control Register - SCSCR1(byte), SCSCR2(half) */ +#define SH7750_SCSCR_REGOFS(n) ((n) == 1 ? 0xE00008 : 0xE80008) /* offset */ +#define SH7750_SCSCR(n) SH7750_P4_REG32(SH7750_SCSCR_REGOFS(n)) +#define SH7750_SCSCR1 SH7750_SCSCR(1) +#define SH7750_SCSCR2 SH7750_SCSCR(2) +#define SH7750_SCSCR_A7(n) SH7750_A7_REG32(SH7750_SCSCR_REGOFS(n)) +#define SH7750_SCSCR1_A7 SH7750_SCSCR_A7(1) +#define SH7750_SCSCR2_A7 SH7750_SCSCR_A7(2) + +#define SH7750_SCSCR_TIE 0x80 /* Transmit Interrupt Enable */ +#define SH7750_SCSCR_RIE 0x40 /* Receive Interrupt Enable */ +#define SH7750_SCSCR_TE 0x20 /* Transmit Enable */ +#define SH7750_SCSCR_RE 0x10 /* Receive Enable */ +#define SH7750_SCSCR1_MPIE 0x08 /* Multiprocessor Interrupt Enable */ +#define SH7750_SCSCR2_REIE 0x08 /* Receive Error Interrupt Enable */ +#define SH7750_SCSCR1_TEIE 0x04 /* Transmit End Interrupt Enable */ +#define SH7750_SCSCR1_CKE 0x03 /* Clock Enable: */ +#define SH7750_SCSCR_CKE_INTCLK 0x00 /* Use Internal Clock */ +#define SH7750_SCSCR_CKE_EXTCLK 0x02 /* Use External Clock from SCK */ +#define SH7750_SCSCR1_CKE_ASYNC_SCK_CLKOUT 0x01 /* Use SCK as a clock output + in asynchronous mode */ + +/* SCI Serial Status Register - SCSSR1(byte), SCSFR2(half) */ +#define SH7750_SCSSR_REGOFS(n) ((n) == 1 ? 0xE00010 : 0xE80010) /* offset */ +#define SH7750_SCSSR(n) SH7750_P4_REG32(SH7750_SCSSR_REGOFS(n)) +#define SH7750_SCSSR1 SH7750_SCSSR(1) +#define SH7750_SCSFR2 SH7750_SCSSR(2) +#define SH7750_SCSSR_A7(n) SH7750_A7_REG32(SH7750_SCSSR_REGOFS(n)) +#define SH7750_SCSSR1_A7 SH7750_SCSSR_A7(1) +#define SH7750_SCSFR2_A7 SH7750_SCSSR_A7(2) + +#define SH7750_SCSSR1_TDRE 0x80 /* Transmit Data Register Empty */ +#define SH7750_SCSSR1_RDRF 0x40 /* Receive Data Register Full */ +#define SH7750_SCSSR1_ORER 0x20 /* Overrun Error */ +#define SH7750_SCSSR1_FER 0x10 /* Framing Error */ +#define SH7750_SCSSR1_PER 0x08 /* Parity Error */ +#define SH7750_SCSSR1_TEND 0x04 /* Transmit End */ +#define SH7750_SCSSR1_MPB 0x02 /* Multiprocessor Bit */ +#define SH7750_SCSSR1_MPBT 0x01 /* Multiprocessor Bit Transfer */ + +#define SH7750_SCFSR2_PERN 0xF000 /* Number of Parity Errors */ +#define SH7750_SCFSR2_PERN_S 12 +#define SH7750_SCFSR2_FERN 0x0F00 /* Number of Framing Errors */ +#define SH7750_SCFSR2_FERN_S 8 +#define SH7750_SCFSR2_ER 0x0080 /* Receive Error */ +#define SH7750_SCFSR2_TEND 0x0040 /* Transmit End */ +#define SH7750_SCFSR2_TDFE 0x0020 /* Transmit FIFO Data Empty */ +#define SH7750_SCFSR2_BRK 0x0010 /* Break Detect */ +#define SH7750_SCFSR2_FER 0x0008 /* Framing Error */ +#define SH7750_SCFSR2_PER 0x0004 /* Parity Error */ +#define SH7750_SCFSR2_RDF 0x0002 /* Receive FIFO Data Full */ +#define SH7750_SCFSR2_DR 0x0001 /* Receive Data Ready */ + +/* SCI Serial Port Register - SCSPTR1(byte) */ +#define SH7750_SCSPTR1_REGOFS 0xE0001C /* offset */ +#define SH7750_SCSPTR1 SH7750_P4_REG32(SH7750_SCSPTR1_REGOFS) +#define SH7750_SCSPTR1_A7 SH7750_A7_REG32(SH7750_SCSPTR1_REGOFS) + +#define SH7750_SCSPTR1_EIO 0x80 /* Error Interrupt Only */ +#define SH7750_SCSPTR1_SPB1IO 0x08 /* 1: Output SPB1DT bit to SCK pin */ +#define SH7750_SCSPTR1_SPB1DT 0x04 /* Serial Port Clock Port Data */ +#define SH7750_SCSPTR1_SPB0IO 0x02 /* 1: Output SPB0DT bit to TxD pin */ +#define SH7750_SCSPTR1_SPB0DT 0x01 /* Serial Port Break Data */ + +/* SCIF Serial Port Register - SCSPTR2(half) */ +#define SH7750_SCSPTR2_REGOFS 0xE80020 /* offset */ +#define SH7750_SCSPTR2 SH7750_P4_REG32(SH7750_SCSPTR2_REGOFS) +#define SH7750_SCSPTR2_A7 SH7750_A7_REG32(SH7750_SCSPTR2_REGOFS) + +#define SH7750_SCSPTR2_RTSIO 0x80 /* 1: Output RTSDT bit to RTS2\ pin */ +#define SH7750_SCSPTR2_RTSDT 0x40 /* RTS Port Data */ +#define SH7750_SCSPTR2_CTSIO 0x20 /* 1: Output CTSDT bit to CTS2\ pin */ +#define SH7750_SCSPTR2_CTSDT 0x10 /* CTS Port Data */ +#define SH7750_SCSPTR2_SPB2IO 0x02 /* 1: Output SPBDT bit to TxD2 pin */ +#define SH7750_SCSPTR2_SPB2DT 0x01 /* Serial Port Break Data */ + +/* SCI Bit Rate Register - SCBRR1(byte), SCBRR2(byte) */ +#define SH7750_SCBRR_REGOFS(n) ((n) == 1 ? 0xE00004 : 0xE80004) /* offset */ +#define SH7750_SCBRR(n) SH7750_P4_REG32(SH7750_SCBRR_REGOFS(n)) +#define SH7750_SCBRR1 SH7750_SCBRR_P4(1) +#define SH7750_SCBRR2 SH7750_SCBRR_P4(2) +#define SH7750_SCBRR_A7(n) SH7750_A7_REG32(SH7750_SCBRR_REGOFS(n)) +#define SH7750_SCBRR1_A7 SH7750_SCBRR_A7(1) +#define SH7750_SCBRR2_A7 SH7750_SCBRR_A7(2) + +/* SCIF FIFO Control Register - SCFCR2(half) */ +#define SH7750_SCFCR2_REGOFS 0xE80018 /* offset */ +#define SH7750_SCFCR2 SH7750_P4_REG32(SH7750_SCFCR2_REGOFS) +#define SH7750_SCFCR2_A7 SH7750_A7_REG32(SH7750_SCFCR2_REGOFS) + +#define SH7750_SCFCR2_RSTRG 0x700 /* RTS2\ Output Active Trigger; RTS2\ + signal goes to high level when the + number of received data stored in + FIFO exceeds the trigger number */ +#define SH7750_SCFCR2_RSTRG_15 0x000 /* 15 bytes */ +#define SH7750_SCFCR2_RSTRG_1 0x000 /* 1 byte */ +#define SH7750_SCFCR2_RSTRG_4 0x000 /* 4 bytes */ +#define SH7750_SCFCR2_RSTRG_6 0x000 /* 6 bytes */ +#define SH7750_SCFCR2_RSTRG_8 0x000 /* 8 bytes */ +#define SH7750_SCFCR2_RSTRG_10 0x000 /* 10 bytes */ +#define SH7750_SCFCR2_RSTRG_14 0x000 /* 14 bytes */ + +#define SH7750_SCFCR2_RTRG 0x0C0 /* Receive FIFO Data Number Trigger, + Receive Data Full (RDF) Flag sets + when number of receive data bytes is + equal or greater than the trigger + number */ +#define SH7750_SCFCR2_RTRG_1 0x000 /* 1 byte */ +#define SH7750_SCFCR2_RTRG_4 0x040 /* 4 bytes */ +#define SH7750_SCFCR2_RTRG_8 0x080 /* 8 bytes */ +#define SH7750_SCFCR2_RTRG_14 0x0C0 /* 14 bytes */ + +#define SH7750_SCFCR2_TTRG 0x030 /* Transmit FIFO Data Number Trigger, + Transmit FIFO Data Register Empty (TDFE) + flag sets when the number of remaining + transmit data bytes is equal or less + than the trigger number */ +#define SH7750_SCFCR2_TTRG_8 0x000 /* 8 bytes */ +#define SH7750_SCFCR2_TTRG_4 0x010 /* 4 bytes */ +#define SH7750_SCFCR2_TTRG_2 0x020 /* 2 bytes */ +#define SH7750_SCFCR2_TTRG_1 0x030 /* 1 byte */ + +#define SH7750_SCFCR2_MCE 0x008 /* Modem Control Enable */ +#define SH7750_SCFCR2_TFRST 0x004 /* Transmit FIFO Data Register Reset, + invalidates the transmit data in the + transmit FIFO */ +#define SH7750_SCFCR2_RFRST 0x002 /* Receive FIFO Data Register Reset, + invalidates the receive data in the + receive FIFO data register and resets + it to the empty state */ +#define SH7750_SCFCR2_LOOP 0x001 /* Loopback Test */ + +/* SCIF FIFO Data Count Register - SCFDR2(half, read-only) */ +#define SH7750_SCFDR2_REGOFS 0xE8001C /* offset */ +#define SH7750_SCFDR2 SH7750_P4_REG32(SH7750_SCFDR2_REGOFS) +#define SH7750_SCFDR2_A7 SH7750_A7_REG32(SH7750_SCFDR2_REGOFS) + +#define SH7750_SCFDR2_T 0x1F00 /* Number of untransmitted data bytes + in transmit FIFO */ +#define SH7750_SCFDR2_T_S 8 +#define SH7750_SCFDR2_R 0x001F /* Number of received data bytes in + receive FIFO */ +#define SH7750_SCFDR2_R_S 0 + +/* SCIF Line Status Register - SCLSR2(half, read-only) */ +#define SH7750_SCLSR2_REGOFS 0xE80024 /* offset */ +#define SH7750_SCLSR2 SH7750_P4_REG32(SH7750_SCLSR2_REGOFS) +#define SH7750_SCLSR2_A7 SH7750_A7_REG32(SH7750_SCLSR2_REGOFS) + +#define SH7750_SCLSR2_ORER 0x0001 /* Overrun Error */ + +/* + * SCI-based Smart Card Interface + */ +/* Smart Card Mode Register - SCSCMR1(byte) */ +#define SH7750_SCSCMR1_REGOFS 0xE00018 /* offset */ +#define SH7750_SCSCMR1 SH7750_P4_REG32(SH7750_SCSCMR1_REGOFS) +#define SH7750_SCSCMR1_A7 SH7750_A7_REG32(SH7750_SCSCMR1_REGOFS) + +#define SH7750_SCSCMR1_SDIR 0x08 /* Smart Card Data Transfer Direction: */ +#define SH7750_SCSCMR1_SDIR_LSBF 0x00 /* LSB-first */ +#define SH7750_SCSCMR1_SDIR_MSBF 0x08 /* MSB-first */ + +#define SH7750_SCSCMR1_SINV 0x04 /* Smart Card Data Inversion */ +#define SH7750_SCSCMR1_SMIF 0x01 /* Smart Card Interface Mode Select */ + +/* Smart-card specific bits in other registers */ +/* SCSMR1: */ +#define SH7750_SCSMR1_GSM 0x80 /* GSM mode select */ + +/* SCSSR1: */ +#define SH7750_SCSSR1_ERS 0x10 /* Error Signal Status */ + +/* + * I/O Ports + */ +/* Port Control Register A - PCTRA */ +#define SH7750_PCTRA_REGOFS 0x80002C /* offset */ +#define SH7750_PCTRA SH7750_P4_REG32(SH7750_PCTRA_REGOFS) +#define SH7750_PCTRA_A7 SH7750_A7_REG32(SH7750_PCTRA_REGOFS) + +#define SH7750_PCTRA_PBPUP(n) 0 /* Bit n is pulled up */ +#define SH7750_PCTRA_PBNPUP(n) (1 << ((n)*2+1)) /* Bit n is not pulled up */ +#define SH7750_PCTRA_PBINP(n) 0 /* Bit n is an input */ +#define SH7750_PCTRA_PBOUT(n) (1 << ((n)*2)) /* Bit n is an output */ + +/* Port Data Register A - PDTRA(half) */ +#define SH7750_PDTRA_REGOFS 0x800030 /* offset */ +#define SH7750_PDTRA SH7750_P4_REG32(SH7750_PDTRA_REGOFS) +#define SH7750_PDTRA_A7 SH7750_A7_REG32(SH7750_PDTRA_REGOFS) + +#define SH7750_PDTRA_BIT(n) (1 << (n)) + +/* Port Control Register B - PCTRB */ +#define SH7750_PCTRB_REGOFS 0x800040 /* offset */ +#define SH7750_PCTRB SH7750_P4_REG32(SH7750_PCTRB_REGOFS) +#define SH7750_PCTRB_A7 SH7750_A7_REG32(SH7750_PCTRB_REGOFS) + +#define SH7750_PCTRB_PBPUP(n) 0 /* Bit n is pulled up */ +#define SH7750_PCTRB_PBNPUP(n) (1 << ((n-16)*2+1)) /* Bit n is not pulled up */ +#define SH7750_PCTRB_PBINP(n) 0 /* Bit n is an input */ +#define SH7750_PCTRB_PBOUT(n) (1 << ((n-16)*2)) /* Bit n is an output */ + +/* Port Data Register B - PDTRB(half) */ +#define SH7750_PDTRB_REGOFS 0x800044 /* offset */ +#define SH7750_PDTRB SH7750_P4_REG32(SH7750_PDTRB_REGOFS) +#define SH7750_PDTRB_A7 SH7750_A7_REG32(SH7750_PDTRB_REGOFS) + +#define SH7750_PDTRB_BIT(n) (1 << ((n)-16)) + +/* GPIO Interrupt Control Register - GPIOIC(half) */ +#define SH7750_GPIOIC_REGOFS 0x800048 /* offset */ +#define SH7750_GPIOIC SH7750_P4_REG32(SH7750_GPIOIC_REGOFS) +#define SH7750_GPIOIC_A7 SH7750_A7_REG32(SH7750_GPIOIC_REGOFS) + +#define SH7750_GPIOIC_PTIREN(n) (1 << (n)) /* Port n is used as a GPIO int */ + +/* + * Interrupt Controller - INTC + */ +/* Interrupt Control Register - ICR (half) */ +#define SH7750_ICR_REGOFS 0xD00000 /* offset */ +#define SH7750_ICR SH7750_P4_REG32(SH7750_ICR_REGOFS) +#define SH7750_ICR_A7 SH7750_A7_REG32(SH7750_ICR_REGOFS) + +#define SH7750_ICR_NMIL 0x8000 /* NMI Input Level */ +#define SH7750_ICR_MAI 0x4000 /* NMI Interrupt Mask */ + +#define SH7750_ICR_NMIB 0x0200 /* NMI Block Mode: */ +#define SH7750_ICR_NMIB_BLK 0x0000 /* NMI requests held pending while + SR.BL bit is set to 1 */ +#define SH7750_ICR_NMIB_NBLK 0x0200 /* NMI requests detected when SR.BL bit + set to 1 */ + +#define SH7750_ICR_NMIE 0x0100 /* NMI Edge Select: */ +#define SH7750_ICR_NMIE_FALL 0x0000 /* Interrupt request detected on falling + edge of NMI input */ +#define SH7750_ICR_NMIE_RISE 0x0100 /* Interrupt request detected on rising + edge of NMI input */ + +#define SH7750_ICR_IRLM 0x0080 /* IRL Pin Mode: */ +#define SH7750_ICR_IRLM_ENC 0x0000 /* IRL\ pins used as a level-encoded + interrupt requests */ +#define SH7750_ICR_IRLM_RAW 0x0080 /* IRL\ pins used as a four independent + interrupt requests */ + +/* Interrupt Priority Register A - IPRA (half) */ +#define SH7750_IPRA_REGOFS 0xD00004 /* offset */ +#define SH7750_IPRA SH7750_P4_REG32(SH7750_IPRA_REGOFS) +#define SH7750_IPRA_A7 SH7750_A7_REG32(SH7750_IPRA_REGOFS) + +#define SH7750_IPRA_TMU0 0xF000 /* TMU0 interrupt priority */ +#define SH7750_IPRA_TMU0_S 12 +#define SH7750_IPRA_TMU1 0x0F00 /* TMU1 interrupt priority */ +#define SH7750_IPRA_TMU1_S 8 +#define SH7750_IPRA_TMU2 0x00F0 /* TMU2 interrupt priority */ +#define SH7750_IPRA_TMU2_S 4 +#define SH7750_IPRA_RTC 0x000F /* RTC interrupt priority */ +#define SH7750_IPRA_RTC_S 0 + +/* Interrupt Priority Register B - IPRB (half) */ +#define SH7750_IPRB_REGOFS 0xD00008 /* offset */ +#define SH7750_IPRB SH7750_P4_REG32(SH7750_IPRB_REGOFS) +#define SH7750_IPRB_A7 SH7750_A7_REG32(SH7750_IPRB_REGOFS) + +#define SH7750_IPRB_WDT 0xF000 /* WDT interrupt priority */ +#define SH7750_IPRB_WDT_S 12 +#define SH7750_IPRB_REF 0x0F00 /* Memory Refresh unit interrupt + priority */ +#define SH7750_IPRB_REF_S 8 +#define SH7750_IPRB_SCI1 0x00F0 /* SCI1 interrupt priority */ +#define SH7750_IPRB_SCI1_S 4 + +/* Interrupt Priority Register ó - IPRó (half) */ +#define SH7750_IPRC_REGOFS 0xD00004 /* offset */ +#define SH7750_IPRC SH7750_P4_REG32(SH7750_IPRC_REGOFS) +#define SH7750_IPRC_A7 SH7750_A7_REG32(SH7750_IPRC_REGOFS) + +#define SH7750_IPRC_GPIO 0xF000 /* GPIO interrupt priority */ +#define SH7750_IPRC_GPIO_S 12 +#define SH7750_IPRC_DMAC 0x0F00 /* DMAC interrupt priority */ +#define SH7750_IPRC_DMAC_S 8 +#define SH7750_IPRC_SCIF 0x00F0 /* SCIF interrupt priority */ +#define SH7750_IPRC_SCIF_S 4 +#define SH7750_IPRC_HUDI 0x000F /* H-UDI interrupt priority */ +#define SH7750_IPRC_HUDI_S 0 + + +/* + * User Break Controller registers + */ +#define SH7750_BARA 0x200000 /* Break address regiser A */ +#define SH7750_BAMRA 0x200004 /* Break address mask regiser A */ +#define SH7750_BBRA 0x200008 /* Break bus cycle regiser A */ +#define SH7750_BARB 0x20000c /* Break address regiser B */ +#define SH7750_BAMRB 0x200010 /* Break address mask regiser B */ +#define SH7750_BBRB 0x200014 /* Break bus cycle regiser B */ +#define SH7750_BASRB 0x000018 /* Break ASID regiser B */ +#define SH7750_BDRB 0x200018 /* Break data regiser B */ +#define SH7750_BDMRB 0x20001c /* Break data mask regiser B */ +#define SH7750_BRCR 0x200020 /* Break control register */ + +#define SH7750_BRCR_UDBE 0x0001 /* User break debug enable bit */ + +/* + * Missing in RTEMS, added for QEMU + */ +#define SH7750_BCR3_A7 0x1f800050 +#define SH7750_BCR4_A7 0x1e0a00f0 +#define SH7750_PRECHARGE0_A7 0x1f900088 +#define SH7750_PRECHARGE1_A7 0x1f940088 + +#endif diff --git a/tools/ioemu/hw/shix.c b/tools/ioemu/hw/shix.c new file mode 100644 index 0000000000..9577c092c2 --- /dev/null +++ b/tools/ioemu/hw/shix.c @@ -0,0 +1,111 @@ +/* + * SHIX 2.0 board description + * + * Copyright (c) 2005 Samuel Tardieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* + Shix 2.0 board by Alexis Polti, described at + http://perso.enst.fr/~polti/realisations/shix20/ + + More information in target-sh4/README.sh4 +*/ +#include "vl.h" + +#define BIOS_FILENAME "shix_bios.bin" +#define BIOS_ADDRESS 0xA0000000 + +void DMA_run(void) +{ + /* XXXXX */ +} + +void irq_info(void) +{ + /* XXXXX */ +} + +void pic_set_irq(int irq, int level) +{ + /* XXXXX */ +} + +void pic_info() +{ + /* XXXXX */ +} + +void vga_update_display() +{ + /* XXXXX */ +} + +void vga_invalidate_display() +{ + /* XXXXX */ +} + +void vga_screen_dump(const char *filename) +{ + /* XXXXX */ +} + +void shix_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState * ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + int ret; + CPUState *env; + struct SH7750State *s; + + printf("Initializing CPU\n"); + env = cpu_init(); + + /* Allocate memory space */ + printf("Allocating ROM\n"); + cpu_register_physical_memory(0x00000000, 0x00004000, IO_MEM_ROM); + printf("Allocating SDRAM 1\n"); + cpu_register_physical_memory(0x08000000, 0x01000000, 0x00004000); + printf("Allocating SDRAM 2\n"); + cpu_register_physical_memory(0x0c000000, 0x01000000, 0x01004000); + + /* Load BIOS in 0 (and access it through P2, 0xA0000000) */ + printf("%s: load BIOS '%s'\n", __func__, BIOS_FILENAME); + ret = load_image(BIOS_FILENAME, phys_ram_base); + if (ret < 0) { /* Check bios size */ + fprintf(stderr, "ret=%d\n", ret); + fprintf(stderr, "qemu: could not load SHIX bios '%s'\n", + BIOS_FILENAME); + exit(1); + } + + /* Register peripherals */ + s = sh7750_init(env); + /* XXXXX Check success */ + tc58128_init(s, "shix_linux_nand.bin", NULL); + fprintf(stderr, "initialization terminated\n"); +} + +QEMUMachine shix_machine = { + "shix", + "shix card", + shix_init +}; diff --git a/tools/ioemu/hw/slavio_intctl.c b/tools/ioemu/hw/slavio_intctl.c new file mode 100644 index 0000000000..e43151fad8 --- /dev/null +++ b/tools/ioemu/hw/slavio_intctl.c @@ -0,0 +1,400 @@ +/* + * QEMU Sparc SLAVIO interrupt controller emulation + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +//#define DEBUG_IRQ_COUNT +//#define DEBUG_IRQ + +#ifdef DEBUG_IRQ +#define DPRINTF(fmt, args...) \ +do { printf("IRQ: " fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) +#endif + +/* + * Registers of interrupt controller in sun4m. + * + * This is the interrupt controller part of chip STP2001 (Slave I/O), also + * produced as NCR89C105. See + * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt + * + * There is a system master controller and one for each cpu. + * + */ + +#define MAX_CPUS 16 + +typedef struct SLAVIO_INTCTLState { + uint32_t intreg_pending[MAX_CPUS]; + uint32_t intregm_pending; + uint32_t intregm_disabled; + uint32_t target_cpu; +#ifdef DEBUG_IRQ_COUNT + uint64_t irq_count[32]; +#endif + CPUState *cpu_envs[MAX_CPUS]; +} SLAVIO_INTCTLState; + +#define INTCTL_MAXADDR 0xf +#define INTCTLM_MAXADDR 0xf +static void slavio_check_interrupts(void *opaque); + +// per-cpu interrupt controller +static uint32_t slavio_intctl_mem_readl(void *opaque, target_phys_addr_t addr) +{ + SLAVIO_INTCTLState *s = opaque; + uint32_t saddr; + int cpu; + + cpu = (addr & (MAX_CPUS - 1) * TARGET_PAGE_SIZE) >> 12; + saddr = (addr & INTCTL_MAXADDR) >> 2; + switch (saddr) { + case 0: + return s->intreg_pending[cpu]; + default: + break; + } + return 0; +} + +static void slavio_intctl_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + SLAVIO_INTCTLState *s = opaque; + uint32_t saddr; + int cpu; + + cpu = (addr & (MAX_CPUS - 1) * TARGET_PAGE_SIZE) >> 12; + saddr = (addr & INTCTL_MAXADDR) >> 2; + switch (saddr) { + case 1: // clear pending softints + if (val & 0x4000) + val |= 80000000; + val &= 0xfffe0000; + s->intreg_pending[cpu] &= ~val; + DPRINTF("Cleared cpu %d irq mask %x, curmask %x\n", cpu, val, s->intreg_pending[cpu]); + break; + case 2: // set softint + val &= 0xfffe0000; + s->intreg_pending[cpu] |= val; + slavio_check_interrupts(s); + DPRINTF("Set cpu %d irq mask %x, curmask %x\n", cpu, val, s->intreg_pending[cpu]); + break; + default: + break; + } +} + +static CPUReadMemoryFunc *slavio_intctl_mem_read[3] = { + slavio_intctl_mem_readl, + slavio_intctl_mem_readl, + slavio_intctl_mem_readl, +}; + +static CPUWriteMemoryFunc *slavio_intctl_mem_write[3] = { + slavio_intctl_mem_writel, + slavio_intctl_mem_writel, + slavio_intctl_mem_writel, +}; + +// master system interrupt controller +static uint32_t slavio_intctlm_mem_readl(void *opaque, target_phys_addr_t addr) +{ + SLAVIO_INTCTLState *s = opaque; + uint32_t saddr; + + saddr = (addr & INTCTLM_MAXADDR) >> 2; + switch (saddr) { + case 0: + return s->intregm_pending & 0x7fffffff; + case 1: + return s->intregm_disabled; + case 4: + return s->target_cpu; + default: + break; + } + return 0; +} + +static void slavio_intctlm_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + SLAVIO_INTCTLState *s = opaque; + uint32_t saddr; + + saddr = (addr & INTCTLM_MAXADDR) >> 2; + switch (saddr) { + case 2: // clear (enable) + // Force clear unused bits + val &= ~0x4fb2007f; + s->intregm_disabled &= ~val; + DPRINTF("Enabled master irq mask %x, curmask %x\n", val, s->intregm_disabled); + slavio_check_interrupts(s); + break; + case 3: // set (disable, clear pending) + // Force clear unused bits + val &= ~0x4fb2007f; + s->intregm_disabled |= val; + s->intregm_pending &= ~val; + DPRINTF("Disabled master irq mask %x, curmask %x\n", val, s->intregm_disabled); + break; + case 4: + s->target_cpu = val & (MAX_CPUS - 1); + DPRINTF("Set master irq cpu %d\n", s->target_cpu); + break; + default: + break; + } +} + +static CPUReadMemoryFunc *slavio_intctlm_mem_read[3] = { + slavio_intctlm_mem_readl, + slavio_intctlm_mem_readl, + slavio_intctlm_mem_readl, +}; + +static CPUWriteMemoryFunc *slavio_intctlm_mem_write[3] = { + slavio_intctlm_mem_writel, + slavio_intctlm_mem_writel, + slavio_intctlm_mem_writel, +}; + +void slavio_pic_info(void *opaque) +{ + SLAVIO_INTCTLState *s = opaque; + int i; + + for (i = 0; i < MAX_CPUS; i++) { + term_printf("per-cpu %d: pending 0x%08x\n", i, s->intreg_pending[i]); + } + term_printf("master: pending 0x%08x, disabled 0x%08x\n", s->intregm_pending, s->intregm_disabled); +} + +void slavio_irq_info(void *opaque) +{ +#ifndef DEBUG_IRQ_COUNT + term_printf("irq statistic code not compiled.\n"); +#else + SLAVIO_INTCTLState *s = opaque; + int i; + int64_t count; + + term_printf("IRQ statistics:\n"); + for (i = 0; i < 32; i++) { + count = s->irq_count[i]; + if (count > 0) + term_printf("%2d: %lld\n", i, count); + } +#endif +} + +static const uint32_t intbit_to_level[32] = { + 2, 3, 5, 7, 9, 11, 0, 14, 3, 5, 7, 9, 11, 13, 12, 12, + 6, 0, 4, 10, 8, 0, 11, 0, 0, 0, 0, 0, 15, 0, 15, 0, +}; + +static void slavio_check_interrupts(void *opaque) +{ + CPUState *env; + SLAVIO_INTCTLState *s = opaque; + uint32_t pending = s->intregm_pending; + unsigned int i, j, max = 0; + + pending &= ~s->intregm_disabled; + + if (pending && !(s->intregm_disabled & 0x80000000)) { + for (i = 0; i < 32; i++) { + if (pending & (1 << i)) { + if (max < intbit_to_level[i]) + max = intbit_to_level[i]; + } + } + env = s->cpu_envs[s->target_cpu]; + if (!env) { + DPRINTF("No CPU %d, not triggered (pending %x)\n", s->target_cpu, pending); + } + else { + if (env->halted) + env->halted = 0; + if (env->interrupt_index == 0) { + DPRINTF("Triggered CPU %d pil %d\n", s->target_cpu, max); +#ifdef DEBUG_IRQ_COUNT + s->irq_count[max]++; +#endif + env->interrupt_index = TT_EXTINT | max; + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } + else + DPRINTF("Not triggered (pending %x), pending exception %x\n", pending, env->interrupt_index); + } + } + else + DPRINTF("Not triggered (pending %x), disabled %x\n", pending, s->intregm_disabled); + + for (i = 0; i < MAX_CPUS; i++) { + max = 0; + env = s->cpu_envs[i]; + if (!env) + continue; + for (j = 17; j < 32; j++) { + if (s->intreg_pending[i] & (1 << j)) { + if (max < j - 16) + max = j - 16; + } + } + if (max > 0) { + if (env->halted) + env->halted = 0; + if (env->interrupt_index == 0) { + DPRINTF("Triggered softint %d for cpu %d (pending %x)\n", max, i, pending); +#ifdef DEBUG_IRQ_COUNT + s->irq_count[max]++; +#endif + env->interrupt_index = TT_EXTINT | max; + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } + } + } +} + +/* + * "irq" here is the bit number in the system interrupt register to + * separate serial and keyboard interrupts sharing a level. + */ +void slavio_pic_set_irq(void *opaque, int irq, int level) +{ + SLAVIO_INTCTLState *s = opaque; + + DPRINTF("Set cpu %d irq %d level %d\n", s->target_cpu, irq, level); + if (irq < 32) { + uint32_t mask = 1 << irq; + uint32_t pil = intbit_to_level[irq]; + if (pil > 0) { + if (level) { + s->intregm_pending |= mask; + s->intreg_pending[s->target_cpu] |= 1 << pil; + } + else { + s->intregm_pending &= ~mask; + s->intreg_pending[s->target_cpu] &= ~(1 << pil); + } + } + } + slavio_check_interrupts(s); +} + +void slavio_pic_set_irq_cpu(void *opaque, int irq, int level, unsigned int cpu) +{ + SLAVIO_INTCTLState *s = opaque; + + DPRINTF("Set cpu %d local irq %d level %d\n", cpu, irq, level); + if (cpu == (unsigned int)-1) { + slavio_pic_set_irq(opaque, irq, level); + return; + } + if (irq < 32) { + uint32_t pil = intbit_to_level[irq]; + if (pil > 0) { + if (level) { + s->intreg_pending[cpu] |= 1 << pil; + } + else { + s->intreg_pending[cpu] &= ~(1 << pil); + } + } + } + slavio_check_interrupts(s); +} + +static void slavio_intctl_save(QEMUFile *f, void *opaque) +{ + SLAVIO_INTCTLState *s = opaque; + int i; + + for (i = 0; i < MAX_CPUS; i++) { + qemu_put_be32s(f, &s->intreg_pending[i]); + } + qemu_put_be32s(f, &s->intregm_pending); + qemu_put_be32s(f, &s->intregm_disabled); + qemu_put_be32s(f, &s->target_cpu); +} + +static int slavio_intctl_load(QEMUFile *f, void *opaque, int version_id) +{ + SLAVIO_INTCTLState *s = opaque; + int i; + + if (version_id != 1) + return -EINVAL; + + for (i = 0; i < MAX_CPUS; i++) { + qemu_get_be32s(f, &s->intreg_pending[i]); + } + qemu_get_be32s(f, &s->intregm_pending); + qemu_get_be32s(f, &s->intregm_disabled); + qemu_get_be32s(f, &s->target_cpu); + return 0; +} + +static void slavio_intctl_reset(void *opaque) +{ + SLAVIO_INTCTLState *s = opaque; + int i; + + for (i = 0; i < MAX_CPUS; i++) { + s->intreg_pending[i] = 0; + } + s->intregm_disabled = ~0xffb2007f; + s->intregm_pending = 0; + s->target_cpu = 0; +} + +void slavio_intctl_set_cpu(void *opaque, unsigned int cpu, CPUState *env) +{ + SLAVIO_INTCTLState *s = opaque; + s->cpu_envs[cpu] = env; +} + +void *slavio_intctl_init(uint32_t addr, uint32_t addrg) +{ + int slavio_intctl_io_memory, slavio_intctlm_io_memory, i; + SLAVIO_INTCTLState *s; + + s = qemu_mallocz(sizeof(SLAVIO_INTCTLState)); + if (!s) + return NULL; + + for (i = 0; i < MAX_CPUS; i++) { + slavio_intctl_io_memory = cpu_register_io_memory(0, slavio_intctl_mem_read, slavio_intctl_mem_write, s); + cpu_register_physical_memory(addr + i * TARGET_PAGE_SIZE, INTCTL_MAXADDR, slavio_intctl_io_memory); + } + + slavio_intctlm_io_memory = cpu_register_io_memory(0, slavio_intctlm_mem_read, slavio_intctlm_mem_write, s); + cpu_register_physical_memory(addrg, INTCTLM_MAXADDR, slavio_intctlm_io_memory); + + register_savevm("slavio_intctl", addr, 1, slavio_intctl_save, slavio_intctl_load, s); + qemu_register_reset(slavio_intctl_reset, s); + slavio_intctl_reset(s); + return s; +} + diff --git a/tools/ioemu/hw/slavio_misc.c b/tools/ioemu/hw/slavio_misc.c new file mode 100644 index 0000000000..904f44e515 --- /dev/null +++ b/tools/ioemu/hw/slavio_misc.c @@ -0,0 +1,244 @@ +/* + * QEMU Sparc SLAVIO aux io port emulation + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +/* debug misc */ +//#define DEBUG_MISC + +/* + * This is the auxio port, chip control and system control part of + * chip STP2001 (Slave I/O), also produced as NCR89C105. See + * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt + * + * This also includes the PMC CPU idle controller. + */ + +#ifdef DEBUG_MISC +#define MISC_DPRINTF(fmt, args...) \ +do { printf("MISC: " fmt , ##args); } while (0) +#else +#define MISC_DPRINTF(fmt, args...) +#endif + +typedef struct MiscState { + int irq; + uint8_t config; + uint8_t aux1, aux2; + uint8_t diag, mctrl, sysctrl; +} MiscState; + +#define MISC_MAXADDR 1 + +static void slavio_misc_update_irq(void *opaque) +{ + MiscState *s = opaque; + + if ((s->aux2 & 0x4) && (s->config & 0x8)) { + pic_set_irq(s->irq, 1); + } else { + pic_set_irq(s->irq, 0); + } +} + +static void slavio_misc_reset(void *opaque) +{ + MiscState *s = opaque; + + // Diagnostic and system control registers not cleared in reset + s->config = s->aux1 = s->aux2 = s->mctrl = 0; +} + +void slavio_set_power_fail(void *opaque, int power_failing) +{ + MiscState *s = opaque; + + MISC_DPRINTF("Power fail: %d, config: %d\n", power_failing, s->config); + if (power_failing && (s->config & 0x8)) { + s->aux2 |= 0x4; + } else { + s->aux2 &= ~0x4; + } + slavio_misc_update_irq(s); +} + +static void slavio_misc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + MiscState *s = opaque; + + switch (addr & 0xfff0000) { + case 0x1800000: + MISC_DPRINTF("Write config %2.2x\n", val & 0xff); + s->config = val & 0xff; + slavio_misc_update_irq(s); + break; + case 0x1900000: + MISC_DPRINTF("Write aux1 %2.2x\n", val & 0xff); + s->aux1 = val & 0xff; + break; + case 0x1910000: + val &= 0x3; + MISC_DPRINTF("Write aux2 %2.2x\n", val); + val |= s->aux2 & 0x4; + if (val & 0x2) // Clear Power Fail int + val &= 0x1; + s->aux2 = val; + if (val & 1) + qemu_system_shutdown_request(); + slavio_misc_update_irq(s); + break; + case 0x1a00000: + MISC_DPRINTF("Write diag %2.2x\n", val & 0xff); + s->diag = val & 0xff; + break; + case 0x1b00000: + MISC_DPRINTF("Write modem control %2.2x\n", val & 0xff); + s->mctrl = val & 0xff; + break; + case 0x1f00000: + MISC_DPRINTF("Write system control %2.2x\n", val & 0xff); + if (val & 1) { + s->sysctrl = 0x2; + qemu_system_reset_request(); + } + break; + case 0xa000000: + MISC_DPRINTF("Write power management %2.2x\n", val & 0xff); +#if 0 + // XXX almost works + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT); +#endif + break; + } +} + +static uint32_t slavio_misc_mem_readb(void *opaque, target_phys_addr_t addr) +{ + MiscState *s = opaque; + uint32_t ret = 0; + + switch (addr & 0xfff0000) { + case 0x1800000: + ret = s->config; + MISC_DPRINTF("Read config %2.2x\n", ret); + break; + case 0x1900000: + ret = s->aux1; + MISC_DPRINTF("Read aux1 %2.2x\n", ret); + break; + case 0x1910000: + ret = s->aux2; + MISC_DPRINTF("Read aux2 %2.2x\n", ret); + break; + case 0x1a00000: + ret = s->diag; + MISC_DPRINTF("Read diag %2.2x\n", ret); + break; + case 0x1b00000: + ret = s->mctrl; + MISC_DPRINTF("Read modem control %2.2x\n", ret); + break; + case 0x1f00000: + MISC_DPRINTF("Read system control %2.2x\n", ret); + ret = s->sysctrl; + break; + case 0xa000000: + MISC_DPRINTF("Read power management %2.2x\n", ret); + break; + } + return ret; +} + +static CPUReadMemoryFunc *slavio_misc_mem_read[3] = { + slavio_misc_mem_readb, + slavio_misc_mem_readb, + slavio_misc_mem_readb, +}; + +static CPUWriteMemoryFunc *slavio_misc_mem_write[3] = { + slavio_misc_mem_writeb, + slavio_misc_mem_writeb, + slavio_misc_mem_writeb, +}; + +static void slavio_misc_save(QEMUFile *f, void *opaque) +{ + MiscState *s = opaque; + + qemu_put_be32s(f, &s->irq); + qemu_put_8s(f, &s->config); + qemu_put_8s(f, &s->aux1); + qemu_put_8s(f, &s->aux2); + qemu_put_8s(f, &s->diag); + qemu_put_8s(f, &s->mctrl); + qemu_put_8s(f, &s->sysctrl); +} + +static int slavio_misc_load(QEMUFile *f, void *opaque, int version_id) +{ + MiscState *s = opaque; + + if (version_id != 1) + return -EINVAL; + + qemu_get_be32s(f, &s->irq); + qemu_get_8s(f, &s->config); + qemu_get_8s(f, &s->aux1); + qemu_get_8s(f, &s->aux2); + qemu_get_8s(f, &s->diag); + qemu_get_8s(f, &s->mctrl); + qemu_get_8s(f, &s->sysctrl); + return 0; +} + +void *slavio_misc_init(uint32_t base, int irq) +{ + int slavio_misc_io_memory; + MiscState *s; + + s = qemu_mallocz(sizeof(MiscState)); + if (!s) + return NULL; + + slavio_misc_io_memory = cpu_register_io_memory(0, slavio_misc_mem_read, slavio_misc_mem_write, s); + // Slavio control + cpu_register_physical_memory(base + 0x1800000, MISC_MAXADDR, slavio_misc_io_memory); + // AUX 1 + cpu_register_physical_memory(base + 0x1900000, MISC_MAXADDR, slavio_misc_io_memory); + // AUX 2 + cpu_register_physical_memory(base + 0x1910000, MISC_MAXADDR, slavio_misc_io_memory); + // Diagnostics + cpu_register_physical_memory(base + 0x1a00000, MISC_MAXADDR, slavio_misc_io_memory); + // Modem control + cpu_register_physical_memory(base + 0x1b00000, MISC_MAXADDR, slavio_misc_io_memory); + // System control + cpu_register_physical_memory(base + 0x1f00000, MISC_MAXADDR, slavio_misc_io_memory); + // Power management + cpu_register_physical_memory(base + 0xa000000, MISC_MAXADDR, slavio_misc_io_memory); + + s->irq = irq; + + register_savevm("slavio_misc", base, 1, slavio_misc_save, slavio_misc_load, s); + qemu_register_reset(slavio_misc_reset, s); + slavio_misc_reset(s); + return s; +} diff --git a/tools/ioemu/hw/slavio_serial.c b/tools/ioemu/hw/slavio_serial.c new file mode 100644 index 0000000000..b13e7c46f9 --- /dev/null +++ b/tools/ioemu/hw/slavio_serial.c @@ -0,0 +1,545 @@ +/* + * QEMU Sparc SLAVIO serial port emulation + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +/* debug serial */ +//#define DEBUG_SERIAL + +/* debug keyboard */ +//#define DEBUG_KBD + +/* debug mouse */ +//#define DEBUG_MOUSE + +/* + * This is the serial port, mouse and keyboard part of chip STP2001 + * (Slave I/O), also produced as NCR89C105. See + * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt + * + * The serial ports implement full AMD AM8530 or Zilog Z8530 chips, + * mouse and keyboard ports don't implement all functions and they are + * only asynchronous. There is no DMA. + * + */ + +#ifdef DEBUG_SERIAL +#define SER_DPRINTF(fmt, args...) \ +do { printf("SER: " fmt , ##args); } while (0) +#define pic_set_irq(irq, level) \ +do { printf("SER: set_irq(%d): %d\n", (irq), (level)); pic_set_irq((irq),(level));} while (0) +#else +#define SER_DPRINTF(fmt, args...) +#endif +#ifdef DEBUG_KBD +#define KBD_DPRINTF(fmt, args...) \ +do { printf("KBD: " fmt , ##args); } while (0) +#else +#define KBD_DPRINTF(fmt, args...) +#endif +#ifdef DEBUG_MOUSE +#define MS_DPRINTF(fmt, args...) \ +do { printf("SER: " fmt , ##args); } while (0) +#else +#define MS_DPRINTF(fmt, args...) +#endif + +typedef enum { + chn_a, chn_b, +} chn_id_t; + +typedef enum { + ser, kbd, mouse, +} chn_type_t; + +#define KBD_QUEUE_SIZE 256 + +typedef struct { + uint8_t data[KBD_QUEUE_SIZE]; + int rptr, wptr, count; +} KBDQueue; + +typedef struct ChannelState { + int irq; + int reg; + int rxint, txint; + chn_id_t chn; // this channel, A (base+4) or B (base+0) + chn_type_t type; + struct ChannelState *otherchn; + uint8_t rx, tx, wregs[16], rregs[16]; + KBDQueue queue; + CharDriverState *chr; +} ChannelState; + +struct SerialState { + struct ChannelState chn[2]; +}; + +#define SERIAL_MAXADDR 7 + +static void handle_kbd_command(ChannelState *s, int val); +static int serial_can_receive(void *opaque); +static void serial_receive_byte(ChannelState *s, int ch); + +static void put_queue(void *opaque, int b) +{ + ChannelState *s = opaque; + KBDQueue *q = &s->queue; + + KBD_DPRINTF("put: 0x%02x\n", b); + if (q->count >= KBD_QUEUE_SIZE) + return; + q->data[q->wptr] = b; + if (++q->wptr == KBD_QUEUE_SIZE) + q->wptr = 0; + q->count++; + serial_receive_byte(s, 0); +} + +static uint32_t get_queue(void *opaque) +{ + ChannelState *s = opaque; + KBDQueue *q = &s->queue; + int val; + + if (q->count == 0) { + return 0; + } else { + val = q->data[q->rptr]; + if (++q->rptr == KBD_QUEUE_SIZE) + q->rptr = 0; + q->count--; + } + KBD_DPRINTF("get 0x%02x\n", val); + if (q->count > 0) + serial_receive_byte(s, 0); + return val; +} + +static void slavio_serial_update_irq(ChannelState *s) +{ + if ((s->wregs[1] & 1) && // interrupts enabled + (((s->wregs[1] & 2) && s->txint == 1) || // tx ints enabled, pending + ((((s->wregs[1] & 0x18) == 8) || ((s->wregs[1] & 0x18) == 0x10)) && + s->rxint == 1) || // rx ints enabled, pending + ((s->wregs[15] & 0x80) && (s->rregs[0] & 0x80)))) { // break int e&p + pic_set_irq(s->irq, 1); + } else { + pic_set_irq(s->irq, 0); + } +} + +static void slavio_serial_reset_chn(ChannelState *s) +{ + int i; + + s->reg = 0; + for (i = 0; i < SERIAL_MAXADDR; i++) { + s->rregs[i] = 0; + s->wregs[i] = 0; + } + s->wregs[4] = 4; + s->wregs[9] = 0xc0; + s->wregs[11] = 8; + s->wregs[14] = 0x30; + s->wregs[15] = 0xf8; + s->rregs[0] = 0x44; + s->rregs[1] = 6; + + s->rx = s->tx = 0; + s->rxint = s->txint = 0; +} + +static void slavio_serial_reset(void *opaque) +{ + SerialState *s = opaque; + slavio_serial_reset_chn(&s->chn[0]); + slavio_serial_reset_chn(&s->chn[1]); +} + +static inline void clr_rxint(ChannelState *s) +{ + s->rxint = 0; + if (s->chn == 0) + s->rregs[3] &= ~0x20; + else { + s->otherchn->rregs[3] &= ~4; + } + slavio_serial_update_irq(s); +} + +static inline void set_rxint(ChannelState *s) +{ + s->rxint = 1; + if (s->chn == 0) + s->rregs[3] |= 0x20; + else { + s->otherchn->rregs[3] |= 4; + } + slavio_serial_update_irq(s); +} + +static inline void clr_txint(ChannelState *s) +{ + s->txint = 0; + if (s->chn == 0) + s->rregs[3] &= ~0x10; + else { + s->otherchn->rregs[3] &= ~2; + } + slavio_serial_update_irq(s); +} + +static inline void set_txint(ChannelState *s) +{ + s->txint = 1; + if (s->chn == 0) + s->rregs[3] |= 0x10; + else { + s->otherchn->rregs[3] |= 2; + } + slavio_serial_update_irq(s); +} + +static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + SerialState *ser = opaque; + ChannelState *s; + uint32_t saddr; + int newreg, channel; + + val &= 0xff; + saddr = (addr & 3) >> 1; + channel = (addr & SERIAL_MAXADDR) >> 2; + s = &ser->chn[channel]; + switch (saddr) { + case 0: + SER_DPRINTF("Write channel %c, reg[%d] = %2.2x\n", channel? 'b' : 'a', s->reg, val & 0xff); + newreg = 0; + switch (s->reg) { + case 0: + newreg = val & 7; + val &= 0x38; + switch (val) { + case 8: + newreg |= 0x8; + break; + case 0x20: + clr_rxint(s); + break; + case 0x28: + clr_txint(s); + break; + case 0x38: + clr_rxint(s); + clr_txint(s); + break; + default: + break; + } + break; + case 1 ... 8: + case 10 ... 15: + s->wregs[s->reg] = val; + break; + case 9: + switch (val & 0xc0) { + case 0: + default: + break; + case 0x40: + slavio_serial_reset_chn(&ser->chn[1]); + return; + case 0x80: + slavio_serial_reset_chn(&ser->chn[0]); + return; + case 0xc0: + slavio_serial_reset(ser); + return; + } + break; + default: + break; + } + if (s->reg == 0) + s->reg = newreg; + else + s->reg = 0; + break; + case 1: + SER_DPRINTF("Write channel %c, ch %d\n", channel? 'b' : 'a', val); + if (s->wregs[5] & 8) { // tx enabled + s->tx = val; + if (s->chr) + qemu_chr_write(s->chr, &s->tx, 1); + else if (s->type == kbd) { + handle_kbd_command(s, val); + } + s->txint = 1; + s->rregs[0] |= 4; // Tx buffer empty + s->rregs[1] |= 1; // All sent + set_txint(s); + slavio_serial_update_irq(s); + } + break; + default: + break; + } +} + +static uint32_t slavio_serial_mem_readb(void *opaque, target_phys_addr_t addr) +{ + SerialState *ser = opaque; + ChannelState *s; + uint32_t saddr; + uint32_t ret; + int channel; + + saddr = (addr & 3) >> 1; + channel = (addr & SERIAL_MAXADDR) >> 2; + s = &ser->chn[channel]; + switch (saddr) { + case 0: + SER_DPRINTF("Read channel %c, reg[%d] = %2.2x\n", channel? 'b' : 'a', s->reg, s->rregs[s->reg]); + ret = s->rregs[s->reg]; + s->reg = 0; + return ret; + case 1: + s->rregs[0] &= ~1; + clr_rxint(s); + if (s->type == kbd) + ret = get_queue(s); + else + ret = s->rx; + SER_DPRINTF("Read channel %c, ch %d\n", channel? 'b' : 'a', ret); + return ret; + default: + break; + } + return 0; +} + +static int serial_can_receive(void *opaque) +{ + ChannelState *s = opaque; + if (((s->wregs[3] & 1) == 0) // Rx not enabled + || ((s->rregs[0] & 1) == 1)) // char already available + return 0; + else + return 1; +} + +static void serial_receive_byte(ChannelState *s, int ch) +{ + SER_DPRINTF("put ch %d\n", ch); + s->rregs[0] |= 1; + s->rx = ch; + set_rxint(s); +} + +static void serial_receive_break(ChannelState *s) +{ + s->rregs[0] |= 0x80; + slavio_serial_update_irq(s); +} + +static void serial_receive1(void *opaque, const uint8_t *buf, int size) +{ + ChannelState *s = opaque; + serial_receive_byte(s, buf[0]); +} + +static void serial_event(void *opaque, int event) +{ + ChannelState *s = opaque; + if (event == CHR_EVENT_BREAK) + serial_receive_break(s); +} + +static CPUReadMemoryFunc *slavio_serial_mem_read[3] = { + slavio_serial_mem_readb, + slavio_serial_mem_readb, + slavio_serial_mem_readb, +}; + +static CPUWriteMemoryFunc *slavio_serial_mem_write[3] = { + slavio_serial_mem_writeb, + slavio_serial_mem_writeb, + slavio_serial_mem_writeb, +}; + +static void slavio_serial_save_chn(QEMUFile *f, ChannelState *s) +{ + qemu_put_be32s(f, &s->irq); + qemu_put_be32s(f, &s->reg); + qemu_put_be32s(f, &s->rxint); + qemu_put_be32s(f, &s->txint); + qemu_put_8s(f, &s->rx); + qemu_put_8s(f, &s->tx); + qemu_put_buffer(f, s->wregs, 16); + qemu_put_buffer(f, s->rregs, 16); +} + +static void slavio_serial_save(QEMUFile *f, void *opaque) +{ + SerialState *s = opaque; + + slavio_serial_save_chn(f, &s->chn[0]); + slavio_serial_save_chn(f, &s->chn[1]); +} + +static int slavio_serial_load_chn(QEMUFile *f, ChannelState *s, int version_id) +{ + if (version_id != 1) + return -EINVAL; + + qemu_get_be32s(f, &s->irq); + qemu_get_be32s(f, &s->reg); + qemu_get_be32s(f, &s->rxint); + qemu_get_be32s(f, &s->txint); + qemu_get_8s(f, &s->rx); + qemu_get_8s(f, &s->tx); + qemu_get_buffer(f, s->wregs, 16); + qemu_get_buffer(f, s->rregs, 16); + return 0; +} + +static int slavio_serial_load(QEMUFile *f, void *opaque, int version_id) +{ + SerialState *s = opaque; + int ret; + + ret = slavio_serial_load_chn(f, &s->chn[0], version_id); + if (ret != 0) + return ret; + ret = slavio_serial_load_chn(f, &s->chn[1], version_id); + return ret; + +} + +SerialState *slavio_serial_init(int base, int irq, CharDriverState *chr1, CharDriverState *chr2) +{ + int slavio_serial_io_memory, i; + SerialState *s; + + s = qemu_mallocz(sizeof(SerialState)); + if (!s) + return NULL; + + slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read, slavio_serial_mem_write, s); + cpu_register_physical_memory(base, SERIAL_MAXADDR, slavio_serial_io_memory); + + s->chn[0].chr = chr1; + s->chn[1].chr = chr2; + + for (i = 0; i < 2; i++) { + s->chn[i].irq = irq; + s->chn[i].chn = 1 - i; + s->chn[i].type = ser; + if (s->chn[i].chr) { + qemu_chr_add_read_handler(s->chn[i].chr, serial_can_receive, serial_receive1, &s->chn[i]); + qemu_chr_add_event_handler(s->chn[i].chr, serial_event); + } + } + s->chn[0].otherchn = &s->chn[1]; + s->chn[1].otherchn = &s->chn[0]; + register_savevm("slavio_serial", base, 1, slavio_serial_save, slavio_serial_load, s); + qemu_register_reset(slavio_serial_reset, s); + slavio_serial_reset(s); + return s; +} + +static const uint8_t keycodes[128] = { + 127, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 89, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 42, 99, 88, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 47, 19, 121, 119, 5, 6, 8, 10, 12, + 14, 16, 17, 18, 7, 98, 23, 68, 69, 70, 71, 91, 92, 93, 125, 112, + 113, 114, 94, 50, 0, 0, 124, 9, 11, 0, 0, 0, 0, 0, 0, 0, + 90, 0, 46, 22, 13, 111, 52, 20, 96, 24, 28, 74, 27, 123, 44, 66, + 0, 45, 2, 4, 48, 0, 0, 21, 0, 0, 0, 0, 0, 120, 122, 67, +}; + +static void sunkbd_event(void *opaque, int ch) +{ + ChannelState *s = opaque; + int release = ch & 0x80; + + ch = keycodes[ch & 0x7f]; + KBD_DPRINTF("Keycode %d (%s)\n", ch, release? "release" : "press"); + put_queue(s, ch | release); +} + +static void handle_kbd_command(ChannelState *s, int val) +{ + KBD_DPRINTF("Command %d\n", val); + switch (val) { + case 1: // Reset, return type code + put_queue(s, 0xff); + put_queue(s, 5); // Type 5 + break; + case 7: // Query layout + put_queue(s, 0xfe); + put_queue(s, 0x20); // XXX, layout? + break; + default: + break; + } +} + +static void sunmouse_event(void *opaque, + int dx, int dy, int dz, int buttons_state) +{ + ChannelState *s = opaque; + int ch; + + // XXX + ch = 0x42; + serial_receive_byte(s, ch); +} + +void slavio_serial_ms_kbd_init(int base, int irq) +{ + int slavio_serial_io_memory, i; + SerialState *s; + + s = qemu_mallocz(sizeof(SerialState)); + if (!s) + return; + for (i = 0; i < 2; i++) { + s->chn[i].irq = irq; + s->chn[i].chn = 1 - i; + s->chn[i].chr = NULL; + } + s->chn[0].otherchn = &s->chn[1]; + s->chn[1].otherchn = &s->chn[0]; + s->chn[0].type = mouse; + s->chn[1].type = kbd; + + slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read, slavio_serial_mem_write, s); + cpu_register_physical_memory(base, SERIAL_MAXADDR, slavio_serial_io_memory); + + qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0); + qemu_add_kbd_event_handler(sunkbd_event, &s->chn[1]); + qemu_register_reset(slavio_serial_reset, s); + slavio_serial_reset(s); +} diff --git a/tools/ioemu/hw/slavio_timer.c b/tools/ioemu/hw/slavio_timer.c new file mode 100644 index 0000000000..d75a76a636 --- /dev/null +++ b/tools/ioemu/hw/slavio_timer.c @@ -0,0 +1,288 @@ +/* + * QEMU Sparc SLAVIO timer controller emulation + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG_TIMER + +#ifdef DEBUG_TIMER +#define DPRINTF(fmt, args...) \ +do { printf("TIMER: " fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) +#endif + +/* + * Registers of hardware timer in sun4m. + * + * This is the timer/counter part of chip STP2001 (Slave I/O), also + * produced as NCR89C105. See + * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt + * + * The 31-bit counter is incremented every 500ns by bit 9. Bits 8..0 + * are zero. Bit 31 is 1 when count has been reached. + * + * Per-CPU timers interrupt local CPU, system timer uses normal + * interrupt routing. + * + */ + +typedef struct SLAVIO_TIMERState { + uint32_t limit, count, counthigh; + int64_t count_load_time; + int64_t expire_time; + int64_t stop_time, tick_offset; + QEMUTimer *irq_timer; + int irq; + int reached, stopped; + int mode; // 0 = processor, 1 = user, 2 = system + unsigned int cpu; +} SLAVIO_TIMERState; + +#define TIMER_MAXADDR 0x1f +#define CNT_FREQ 2000000 + +// Update count, set irq, update expire_time +static void slavio_timer_get_out(SLAVIO_TIMERState *s) +{ + int out; + int64_t diff, ticks, count; + uint32_t limit; + + // There are three clock tick units: CPU ticks, register units + // (nanoseconds), and counter ticks (500 ns). + if (s->mode == 1 && s->stopped) + ticks = s->stop_time; + else + ticks = qemu_get_clock(vm_clock) - s->tick_offset; + + out = (ticks > s->expire_time); + if (out) + s->reached = 0x80000000; + if (!s->limit) + limit = 0x7fffffff; + else + limit = s->limit; + + // Convert register units to counter ticks + limit = limit >> 9; + + // Convert cpu ticks to counter ticks + diff = muldiv64(ticks - s->count_load_time, CNT_FREQ, ticks_per_sec); + + // Calculate what the counter should be, convert to register + // units + count = diff % limit; + s->count = count << 9; + s->counthigh = count >> 22; + + // Expire time: CPU ticks left to next interrupt + // Convert remaining counter ticks to CPU ticks + s->expire_time = ticks + muldiv64(limit - count, ticks_per_sec, CNT_FREQ); + + DPRINTF("irq %d limit %d reached %d d %lld count %d s->c %x diff %lld stopped %d mode %d\n", s->irq, limit, s->reached?1:0, (ticks-s->count_load_time), count, s->count, s->expire_time - ticks, s->stopped, s->mode); + + if (s->mode != 1) + pic_set_irq_cpu(s->irq, out, s->cpu); +} + +// timer callback +static void slavio_timer_irq(void *opaque) +{ + SLAVIO_TIMERState *s = opaque; + + if (!s->irq_timer) + return; + slavio_timer_get_out(s); + if (s->mode != 1) + qemu_mod_timer(s->irq_timer, s->expire_time); +} + +static uint32_t slavio_timer_mem_readl(void *opaque, target_phys_addr_t addr) +{ + SLAVIO_TIMERState *s = opaque; + uint32_t saddr; + + saddr = (addr & TIMER_MAXADDR) >> 2; + switch (saddr) { + case 0: + // read limit (system counter mode) or read most signifying + // part of counter (user mode) + if (s->mode != 1) { + // clear irq + pic_set_irq_cpu(s->irq, 0, s->cpu); + s->count_load_time = qemu_get_clock(vm_clock); + s->reached = 0; + return s->limit; + } + else { + slavio_timer_get_out(s); + return s->counthigh & 0x7fffffff; + } + case 1: + // read counter and reached bit (system mode) or read lsbits + // of counter (user mode) + slavio_timer_get_out(s); + if (s->mode != 1) + return (s->count & 0x7fffffff) | s->reached; + else + return s->count; + case 3: + // read start/stop status + return s->stopped; + case 4: + // read user/system mode + return s->mode & 1; + default: + return 0; + } +} + +static void slavio_timer_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + SLAVIO_TIMERState *s = opaque; + uint32_t saddr; + + saddr = (addr & TIMER_MAXADDR) >> 2; + switch (saddr) { + case 0: + // set limit, reset counter + s->count_load_time = qemu_get_clock(vm_clock); + // fall through + case 2: + // set limit without resetting counter + if (!val) + s->limit = 0x7fffffff; + else + s->limit = val & 0x7fffffff; + slavio_timer_irq(s); + break; + case 3: + // start/stop user counter + if (s->mode == 1) { + if (val & 1) { + s->stop_time = qemu_get_clock(vm_clock); + s->stopped = 1; + } + else { + if (s->stopped) + s->tick_offset += qemu_get_clock(vm_clock) - s->stop_time; + s->stopped = 0; + } + } + break; + case 4: + // bit 0: user (1) or system (0) counter mode + if (s->mode == 0 || s->mode == 1) + s->mode = val & 1; + break; + default: + break; + } +} + +static CPUReadMemoryFunc *slavio_timer_mem_read[3] = { + slavio_timer_mem_readl, + slavio_timer_mem_readl, + slavio_timer_mem_readl, +}; + +static CPUWriteMemoryFunc *slavio_timer_mem_write[3] = { + slavio_timer_mem_writel, + slavio_timer_mem_writel, + slavio_timer_mem_writel, +}; + +static void slavio_timer_save(QEMUFile *f, void *opaque) +{ + SLAVIO_TIMERState *s = opaque; + + qemu_put_be32s(f, &s->limit); + qemu_put_be32s(f, &s->count); + qemu_put_be32s(f, &s->counthigh); + qemu_put_be64s(f, &s->count_load_time); + qemu_put_be64s(f, &s->expire_time); + qemu_put_be64s(f, &s->stop_time); + qemu_put_be64s(f, &s->tick_offset); + qemu_put_be32s(f, &s->irq); + qemu_put_be32s(f, &s->reached); + qemu_put_be32s(f, &s->stopped); + qemu_put_be32s(f, &s->mode); +} + +static int slavio_timer_load(QEMUFile *f, void *opaque, int version_id) +{ + SLAVIO_TIMERState *s = opaque; + + if (version_id != 1) + return -EINVAL; + + qemu_get_be32s(f, &s->limit); + qemu_get_be32s(f, &s->count); + qemu_get_be32s(f, &s->counthigh); + qemu_get_be64s(f, &s->count_load_time); + qemu_get_be64s(f, &s->expire_time); + qemu_get_be64s(f, &s->stop_time); + qemu_get_be64s(f, &s->tick_offset); + qemu_get_be32s(f, &s->irq); + qemu_get_be32s(f, &s->reached); + qemu_get_be32s(f, &s->stopped); + qemu_get_be32s(f, &s->mode); + return 0; +} + +static void slavio_timer_reset(void *opaque) +{ + SLAVIO_TIMERState *s = opaque; + + s->limit = 0; + s->count = 0; + s->count_load_time = qemu_get_clock(vm_clock);; + s->stop_time = s->count_load_time; + s->tick_offset = 0; + s->reached = 0; + s->mode &= 2; + s->stopped = 1; + slavio_timer_get_out(s); +} + +void slavio_timer_init(uint32_t addr, int irq, int mode, unsigned int cpu) +{ + int slavio_timer_io_memory; + SLAVIO_TIMERState *s; + + s = qemu_mallocz(sizeof(SLAVIO_TIMERState)); + if (!s) + return; + s->irq = irq; + s->mode = mode; + s->cpu = cpu; + s->irq_timer = qemu_new_timer(vm_clock, slavio_timer_irq, s); + + slavio_timer_io_memory = cpu_register_io_memory(0, slavio_timer_mem_read, + slavio_timer_mem_write, s); + cpu_register_physical_memory(addr, TIMER_MAXADDR, slavio_timer_io_memory); + register_savevm("slavio_timer", addr, 1, slavio_timer_save, slavio_timer_load, s); + qemu_register_reset(slavio_timer_reset, s); + slavio_timer_reset(s); +} diff --git a/tools/ioemu/hw/smc91c111.c b/tools/ioemu/hw/smc91c111.c new file mode 100644 index 0000000000..214e92efc0 --- /dev/null +++ b/tools/ioemu/hw/smc91c111.c @@ -0,0 +1,714 @@ +/* + * SMSC 91C111 Ethernet interface emulation + * + * Copyright (c) 2005 CodeSourcery, LLC. + * Written by Paul Brook + * + * This code is licenced under the GPL + */ + +#include "vl.h" +/* For crc32 */ +#include + +/* Number of 2k memory pages available. */ +#define NUM_PACKETS 4 + +typedef struct { + uint32_t base; + VLANClientState *vc; + uint16_t tcr; + uint16_t rcr; + uint16_t cr; + uint16_t ctr; + uint16_t gpr; + uint16_t ptr; + uint16_t ercv; + void *pic; + int irq; + int bank; + int packet_num; + int tx_alloc; + /* Bitmask of allocated packets. */ + int allocated; + int tx_fifo_len; + int tx_fifo[NUM_PACKETS]; + int rx_fifo_len; + int rx_fifo[NUM_PACKETS]; + int tx_fifo_done_len; + int tx_fifo_done[NUM_PACKETS]; + /* Packet buffer memory. */ + uint8_t data[NUM_PACKETS][2048]; + uint8_t int_level; + uint8_t int_mask; + uint8_t macaddr[6]; +} smc91c111_state; + +#define RCR_SOFT_RST 0x8000 +#define RCR_STRIP_CRC 0x0200 +#define RCR_RXEN 0x0100 + +#define TCR_EPH_LOOP 0x2000 +#define TCR_NOCRC 0x0100 +#define TCR_PAD_EN 0x0080 +#define TCR_FORCOL 0x0004 +#define TCR_LOOP 0x0002 +#define TCR_TXEN 0x0001 + +#define INT_MD 0x80 +#define INT_ERCV 0x40 +#define INT_EPH 0x20 +#define INT_RX_OVRN 0x10 +#define INT_ALLOC 0x08 +#define INT_TX_EMPTY 0x04 +#define INT_TX 0x02 +#define INT_RCV 0x01 + +#define CTR_AUTO_RELEASE 0x0800 +#define CTR_RELOAD 0x0002 +#define CTR_STORE 0x0001 + +#define RS_ALGNERR 0x8000 +#define RS_BRODCAST 0x4000 +#define RS_BADCRC 0x2000 +#define RS_ODDFRAME 0x1000 +#define RS_TOOLONG 0x0800 +#define RS_TOOSHORT 0x0400 +#define RS_MULTICAST 0x0001 + +/* Update interrupt status. */ +static void smc91c111_update(smc91c111_state *s) +{ + int level; + + if (s->tx_fifo_len == 0) + s->int_level |= INT_TX_EMPTY; + if (s->tx_fifo_done_len != 0) + s->int_level |= INT_TX; + level = (s->int_level & s->int_mask) != 0; + pic_set_irq_new(s->pic, s->irq, level); +} + +/* Try to allocate a packet. Returns 0x80 on failure. */ +static int smc91c111_allocate_packet(smc91c111_state *s) +{ + int i; + if (s->allocated == (1 << NUM_PACKETS) - 1) { + return 0x80; + } + + for (i = 0; i < NUM_PACKETS; i++) { + if ((s->allocated & (1 << i)) == 0) + break; + } + s->allocated |= 1 << i; + return i; +} + + +/* Process a pending TX allocate. */ +static void smc91c111_tx_alloc(smc91c111_state *s) +{ + s->tx_alloc = smc91c111_allocate_packet(s); + if (s->tx_alloc == 0x80) + return; + s->int_level |= INT_ALLOC; + smc91c111_update(s); +} + +/* Remove and item from the RX FIFO. */ +static void smc91c111_pop_rx_fifo(smc91c111_state *s) +{ + int i; + + s->rx_fifo_len--; + if (s->rx_fifo_len) { + for (i = 0; i < s->rx_fifo_len; i++) + s->rx_fifo[i] = s->rx_fifo[i + 1]; + s->int_level |= INT_RCV; + } else { + s->int_level &= ~INT_RCV; + } + smc91c111_update(s); +} + +/* Remove an item from the TX completion FIFO. */ +static void smc91c111_pop_tx_fifo_done(smc91c111_state *s) +{ + int i; + + if (s->tx_fifo_done_len == 0) + return; + s->tx_fifo_done_len--; + for (i = 0; i < s->tx_fifo_done_len; i++) + s->tx_fifo_done[i] = s->tx_fifo_done[i + 1]; +} + +/* Release the memory allocated to a packet. */ +static void smc91c111_release_packet(smc91c111_state *s, int packet) +{ + s->allocated &= ~(1 << packet); + if (s->tx_alloc == 0x80) + smc91c111_tx_alloc(s); +} + +/* Flush the TX FIFO. */ +static void smc91c111_do_tx(smc91c111_state *s) +{ + int i; + int len; + int control; + int add_crc; + uint32_t crc; + int packetnum; + uint8_t *p; + + if ((s->tcr & TCR_TXEN) == 0) + return; + if (s->tx_fifo_len == 0) + return; + for (i = 0; i < s->tx_fifo_len; i++) { + packetnum = s->tx_fifo[i]; + p = &s->data[packetnum][0]; + /* Set status word. */ + *(p++) = 0x01; + *(p++) = 0x40; + len = *(p++); + len |= ((int)*(p++)) << 8; + len -= 6; + control = p[len + 1]; + if (control & 0x20) + len++; + /* ??? This overwrites the data following the buffer. + Don't know what real hardware does. */ + if (len < 64 && (s->tcr & TCR_PAD_EN)) { + memset(p + len, 0, 64 - len); + len = 64; + } +#if 0 + /* The card is supposed to append the CRC to the frame. However + none of the other network traffic has the CRC appended. + Suspect this is low level ethernet detail we don't need to worry + about. */ + add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0; + if (add_crc) { + crc = crc32(~0, p, len); + memcpy(p + len, &crc, 4); + len += 4; + } +#else + add_crc = 0; +#endif + if (s->ctr & CTR_AUTO_RELEASE) + /* Race? */ + smc91c111_release_packet(s, packetnum); + else if (s->tx_fifo_done_len < NUM_PACKETS) + s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum; + qemu_send_packet(s->vc, p, len); + } + s->tx_fifo_len = 0; + smc91c111_update(s); +} + +/* Add a packet to the TX FIFO. */ +static void smc91c111_queue_tx(smc91c111_state *s, int packet) +{ + if (s->tx_fifo_len == NUM_PACKETS) + return; + s->tx_fifo[s->tx_fifo_len++] = packet; + smc91c111_do_tx(s); +} + +static void smc91c111_reset(smc91c111_state *s) +{ + s->bank = 0; + s->tx_fifo_len = 0; + s->tx_fifo_done_len = 0; + s->rx_fifo_len = 0; + s->allocated = 0; + s->packet_num = 0; + s->tx_alloc = 0; + s->tcr = 0; + s->rcr = 0; + s->cr = 0xa0b1; + s->ctr = 0x1210; + s->ptr = 0; + s->ercv = 0x1f; + s->int_level = INT_TX_EMPTY; + s->int_mask = 0; + smc91c111_update(s); +} + +#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val +#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8) + +static void smc91c111_writeb(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + smc91c111_state *s = (smc91c111_state *)opaque; + + offset -= s->base; + if (offset == 14) { + s->bank = value; + return; + } + if (offset == 15) + return; + switch (s->bank) { + case 0: + switch (offset) { + case 0: /* TCR */ + SET_LOW(tcr, value); + return; + case 1: + SET_HIGH(tcr, value); + return; + case 4: /* RCR */ + SET_LOW(rcr, value); + return; + case 5: + SET_HIGH(rcr, value); + if (s->rcr & RCR_SOFT_RST) + smc91c111_reset(s); + return; + case 10: case 11: /* RPCR */ + /* Ignored */ + return; + } + break; + + case 1: + switch (offset) { + case 0: /* CONFIG */ + SET_LOW(cr, value); + return; + case 1: + SET_HIGH(cr,value); + return; + case 2: case 3: /* BASE */ + case 4: case 5: case 6: case 7: case 8: case 9: /* IA */ + /* Not implemented. */ + return; + case 10: /* Genral Purpose */ + SET_LOW(gpr, value); + return; + case 11: + SET_HIGH(gpr, value); + return; + case 12: /* Control */ + if (value & 1) + fprintf(stderr, "smc91c111:EEPROM store not implemented\n"); + if (value & 2) + fprintf(stderr, "smc91c111:EEPROM reload not implemented\n"); + value &= ~3; + SET_LOW(ctr, value); + return; + case 13: + SET_HIGH(ctr, value); + return; + } + break; + + case 2: + switch (offset) { + case 0: /* MMU Command */ + switch (value >> 5) { + case 0: /* no-op */ + break; + case 1: /* Allocate for TX. */ + s->tx_alloc = 0x80; + s->int_level &= ~INT_ALLOC; + smc91c111_update(s); + smc91c111_tx_alloc(s); + break; + case 2: /* Reset MMU. */ + s->allocated = 0; + s->tx_fifo_len = 0; + s->tx_fifo_done_len = 0; + s->rx_fifo_len = 0; + s->tx_alloc = 0; + break; + case 3: /* Remove from RX FIFO. */ + smc91c111_pop_rx_fifo(s); + break; + case 4: /* Remove from RX FIFO and release. */ + if (s->rx_fifo_len > 0) { + smc91c111_release_packet(s, s->rx_fifo[0]); + } + smc91c111_pop_rx_fifo(s); + break; + case 5: /* Release. */ + smc91c111_release_packet(s, s->packet_num); + break; + case 6: /* Add to TX FIFO. */ + smc91c111_queue_tx(s, s->packet_num); + break; + case 7: /* Reset TX FIFO. */ + s->tx_fifo_len = 0; + s->tx_fifo_done_len = 0; + break; + } + return; + case 1: + /* Ignore. */ + return; + case 2: /* Packet Number Register */ + s->packet_num = value; + return; + case 3: case 4: case 5: + /* Should be readonly, but linux writes to them anyway. Ignore. */ + return; + case 6: /* Pointer */ + SET_LOW(ptr, value); + return; + case 7: + SET_HIGH(ptr, value); + return; + case 8: case 9: case 10: case 11: /* Data */ + { + int p; + int n; + + if (s->ptr & 0x8000) + n = s->rx_fifo[0]; + else + n = s->packet_num; + p = s->ptr & 0x07ff; + if (s->ptr & 0x4000) { + s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff); + } else { + p += (offset & 3); + } + s->data[n][p] = value; + } + return; + case 12: /* Interrupt ACK. */ + s->int_level &= ~(value & 0xd6); + if (value & INT_TX) + smc91c111_pop_tx_fifo_done(s); + smc91c111_update(s); + return; + case 13: /* Interrupt mask. */ + s->int_mask = value; + smc91c111_update(s); + return; + } + break;; + + case 3: + switch (offset) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + /* Multicast table. */ + /* Not implemented. */ + return; + case 8: case 9: /* Management Interface. */ + /* Not implemented. */ + return; + case 12: /* Early receive. */ + s->ercv = value & 0x1f; + case 13: + /* Ignore. */ + return; + } + break; + } + cpu_abort (cpu_single_env, "smc91c111_write: Bad reg %d:%x\n", + s->bank, offset); +} + +static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset) +{ + smc91c111_state *s = (smc91c111_state *)opaque; + + offset -= s->base; + if (offset == 14) { + return s->bank; + } + if (offset == 15) + return 0x33; + switch (s->bank) { + case 0: + switch (offset) { + case 0: /* TCR */ + return s->tcr & 0xff; + case 1: + return s->tcr >> 8; + case 2: /* EPH Status */ + return 0; + case 3: + return 0x40; + case 4: /* RCR */ + return s->rcr & 0xff; + case 5: + return s->rcr >> 8; + case 6: /* Counter */ + case 7: + /* Not implemented. */ + return 0; + case 8: /* Free memory available. */ + { + int i; + int n; + n = 0; + for (i = 0; i < NUM_PACKETS; i++) { + if (s->allocated & (1 << i)) + n++; + } + return n; + } + case 9: /* Memory size. */ + return NUM_PACKETS; + case 10: case 11: /* RPCR */ + /* Not implemented. */ + return 0; + } + break; + + case 1: + switch (offset) { + case 0: /* CONFIG */ + return s->cr & 0xff; + case 1: + return s->cr >> 8; + case 2: case 3: /* BASE */ + /* Not implemented. */ + return 0; + case 4: case 5: case 6: case 7: case 8: case 9: /* IA */ + return s->macaddr[offset - 4]; + case 10: /* General Purpose */ + return s->gpr & 0xff; + case 11: + return s->gpr >> 8; + case 12: /* Control */ + return s->ctr & 0xff; + case 13: + return s->ctr >> 8; + } + break; + + case 2: + switch (offset) { + case 0: case 1: /* MMUCR Busy bit. */ + return 0; + case 2: /* Packet Number. */ + return s->packet_num; + case 3: /* Allocation Result. */ + return s->tx_alloc; + case 4: /* TX FIFO */ + if (s->tx_fifo_done_len == 0) + return 0x80; + else + return s->tx_fifo_done[0]; + case 5: /* RX FIFO */ + if (s->rx_fifo_len == 0) + return 0x80; + else + return s->rx_fifo[0]; + case 6: /* Pointer */ + return s->ptr & 0xff; + case 7: + return (s->ptr >> 8) & 0xf7; + case 8: case 9: case 10: case 11: /* Data */ + { + int p; + int n; + + if (s->ptr & 0x8000) + n = s->rx_fifo[0]; + else + n = s->packet_num; + p = s->ptr & 0x07ff; + if (s->ptr & 0x4000) { + s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff); + } else { + p += (offset & 3); + } + return s->data[n][p]; + } + case 12: /* Interrupt status. */ + return s->int_level; + case 13: /* Interrupt mask. */ + return s->int_mask; + } + break; + + case 3: + switch (offset) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + /* Multicast table. */ + /* Not implemented. */ + return 0; + case 8: /* Management Interface. */ + /* Not implemented. */ + return 0x30; + case 9: + return 0x33; + case 10: /* Revision. */ + return 0x91; + case 11: + return 0x33; + case 12: + return s->ercv; + case 13: + return 0; + } + break; + } + cpu_abort (cpu_single_env, "smc91c111_read: Bad reg %d:%x\n", + s->bank, offset); + return 0; +} + +static void smc91c111_writew(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + smc91c111_writeb(opaque, offset, value & 0xff); + smc91c111_writeb(opaque, offset + 1, value >> 8); +} + +static void smc91c111_writel(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + smc91c111_state *s = (smc91c111_state *)opaque; + /* 32-bit writes to offset 0xc only actually write to the bank select + register (offset 0xe) */ + if (offset != s->base + 0xc) + smc91c111_writew(opaque, offset, value & 0xffff); + smc91c111_writew(opaque, offset + 2, value >> 16); +} + +static uint32_t smc91c111_readw(void *opaque, target_phys_addr_t offset) +{ + uint32_t val; + val = smc91c111_readb(opaque, offset); + val |= smc91c111_readb(opaque, offset + 1) << 8; + return val; +} + +static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset) +{ + uint32_t val; + val = smc91c111_readw(opaque, offset); + val |= smc91c111_readw(opaque, offset + 2) << 16; + return val; +} + +static int smc91c111_can_receive(void *opaque) +{ + smc91c111_state *s = (smc91c111_state *)opaque; + + if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) + return 1; + if (s->allocated == (1 << NUM_PACKETS) - 1) + return 0; + return 1; +} + +static void smc91c111_receive(void *opaque, const uint8_t *buf, int size) +{ + smc91c111_state *s = (smc91c111_state *)opaque; + int status; + int packetsize; + uint32_t crc; + int packetnum; + uint8_t *p; + + if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) + return; + /* Short packets are padded with zeros. Recieveing a packet + < 64 bytes long is considered an error condition. */ + if (size < 64) + packetsize = 64; + else + packetsize = (size & ~1); + packetsize += 6; + crc = (s->rcr & RCR_STRIP_CRC) == 0; + if (crc) + packetsize += 4; + /* TODO: Flag overrun and receive errors. */ + if (packetsize > 2048) + return; + packetnum = smc91c111_allocate_packet(s); + if (packetnum == 0x80) + return; + s->rx_fifo[s->rx_fifo_len++] = packetnum; + + p = &s->data[packetnum][0]; + /* ??? Multicast packets? */ + status = 0; + if (size > 1518) + status |= RS_TOOLONG; + if (size & 1) + status |= RS_ODDFRAME; + *(p++) = status & 0xff; + *(p++) = status >> 8; + *(p++) = packetsize & 0xff; + *(p++) = packetsize >> 8; + memcpy(p, buf, size & ~1); + p += (size & ~1); + /* Pad short packets. */ + if (size < 64) { + int pad; + + if (size & 1) + *(p++) = buf[size - 1]; + pad = 64 - size; + memset(p, 0, pad); + p += pad; + size = 64; + } + /* It's not clear if the CRC should go before or after the last byte in + odd sized packets. Linux disables the CRC, so that's no help. + The pictures in the documentation show the CRC aligned on a 16-bit + boundary before the last odd byte, so that's what we do. */ + if (crc) { + crc = crc32(~0, buf, size); + *(p++) = crc & 0xff; crc >>= 8; + *(p++) = crc & 0xff; crc >>= 8; + *(p++) = crc & 0xff; crc >>= 8; + *(p++) = crc & 0xff; crc >>= 8; + } + if (size & 1) { + *(p++) = buf[size - 1]; + *(p++) = 0x60; + } else { + *(p++) = 0; + *(p++) = 0x40; + } + /* TODO: Raise early RX interrupt? */ + s->int_level |= INT_RCV; + smc91c111_update(s); +} + +static CPUReadMemoryFunc *smc91c111_readfn[] = { + smc91c111_readb, + smc91c111_readw, + smc91c111_readl +}; + +static CPUWriteMemoryFunc *smc91c111_writefn[] = { + smc91c111_writeb, + smc91c111_writew, + smc91c111_writel +}; + +void smc91c111_init(NICInfo *nd, uint32_t base, void *pic, int irq) +{ + smc91c111_state *s; + int iomemtype; + + s = (smc91c111_state *)qemu_mallocz(sizeof(smc91c111_state)); + iomemtype = cpu_register_io_memory(0, smc91c111_readfn, + smc91c111_writefn, s); + cpu_register_physical_memory(base, 16, iomemtype); + s->base = base; + s->pic = pic; + s->irq = irq; + memcpy(s->macaddr, nd->macaddr, 6); + + smc91c111_reset(s); + + s->vc = qemu_new_vlan_client(nd->vlan, smc91c111_receive, + smc91c111_can_receive, s); + /* ??? Save/restore. */ +} diff --git a/tools/ioemu/hw/sun4m.c b/tools/ioemu/hw/sun4m.c new file mode 100644 index 0000000000..3619005d71 --- /dev/null +++ b/tools/ioemu/hw/sun4m.c @@ -0,0 +1,324 @@ +/* + * QEMU Sun4m System Emulator + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#define KERNEL_LOAD_ADDR 0x00004000 +#define CMDLINE_ADDR 0x007ff000 +#define INITRD_LOAD_ADDR 0x00800000 +#define PROM_SIZE_MAX (256 * 1024) +#define PROM_ADDR 0xffd00000 +#define PROM_FILENAMEB "proll.bin" +#define PROM_FILENAMEE "proll.elf" +#define PHYS_JJ_EEPROM 0x71200000 /* m48t08 */ +#define PHYS_JJ_IDPROM_OFF 0x1FD8 +#define PHYS_JJ_EEPROM_SIZE 0x2000 +// IRQs are not PIL ones, but master interrupt controller register +// bits +#define PHYS_JJ_IOMMU 0x10000000 /* I/O MMU */ +#define PHYS_JJ_TCX_FB 0x50000000 /* TCX frame buffer */ +#define PHYS_JJ_SLAVIO 0x70000000 /* Slavio base */ +#define PHYS_JJ_ESPDMA 0x78400000 /* ESP DMA controller */ +#define PHYS_JJ_ESP 0x78800000 /* ESP SCSI */ +#define PHYS_JJ_ESP_IRQ 18 +#define PHYS_JJ_LEDMA 0x78400010 /* Lance DMA controller */ +#define PHYS_JJ_LE 0x78C00000 /* Lance ethernet */ +#define PHYS_JJ_LE_IRQ 16 +#define PHYS_JJ_CLOCK 0x71D00000 /* Per-CPU timer/counter, L14 */ +#define PHYS_JJ_CLOCK_IRQ 7 +#define PHYS_JJ_CLOCK1 0x71D10000 /* System timer/counter, L10 */ +#define PHYS_JJ_CLOCK1_IRQ 19 +#define PHYS_JJ_INTR0 0x71E00000 /* Per-CPU interrupt control registers */ +#define PHYS_JJ_INTR_G 0x71E10000 /* Master interrupt control registers */ +#define PHYS_JJ_MS_KBD 0x71000000 /* Mouse and keyboard */ +#define PHYS_JJ_MS_KBD_IRQ 14 +#define PHYS_JJ_SER 0x71100000 /* Serial */ +#define PHYS_JJ_SER_IRQ 15 +#define PHYS_JJ_FDC 0x71400000 /* Floppy */ +#define PHYS_JJ_FLOPPY_IRQ 22 +#define PHYS_JJ_ME_IRQ 30 /* Module error, power fail */ +#define MAX_CPUS 16 + +/* TSC handling */ + +uint64_t cpu_get_tsc() +{ + return qemu_get_clock(vm_clock); +} + +int DMA_get_channel_mode (int nchan) +{ + return 0; +} +int DMA_read_memory (int nchan, void *buf, int pos, int size) +{ + return 0; +} +int DMA_write_memory (int nchan, void *buf, int pos, int size) +{ + return 0; +} +void DMA_hold_DREQ (int nchan) {} +void DMA_release_DREQ (int nchan) {} +void DMA_schedule(int nchan) {} +void DMA_run (void) {} +void DMA_init (int high_page_enable) {} +void DMA_register_channel (int nchan, + DMA_transfer_handler transfer_handler, + void *opaque) +{ +} + +static void nvram_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value) +{ + m48t59_write(nvram, addr++, (value >> 8) & 0xff); + m48t59_write(nvram, addr++, value & 0xff); +} + +static void nvram_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value) +{ + m48t59_write(nvram, addr++, value >> 24); + m48t59_write(nvram, addr++, (value >> 16) & 0xff); + m48t59_write(nvram, addr++, (value >> 8) & 0xff); + m48t59_write(nvram, addr++, value & 0xff); +} + +static void nvram_set_string (m48t59_t *nvram, uint32_t addr, + const unsigned char *str, uint32_t max) +{ + unsigned int i; + + for (i = 0; i < max && str[i] != '\0'; i++) { + m48t59_write(nvram, addr + i, str[i]); + } + m48t59_write(nvram, addr + max - 1, '\0'); +} + +static m48t59_t *nvram; + +extern int nographic; + +static void nvram_init(m48t59_t *nvram, uint8_t *macaddr, const char *cmdline, + int boot_device, uint32_t RAM_size, + uint32_t kernel_size, + int width, int height, int depth) +{ + unsigned char tmp = 0; + int i, j; + + // Try to match PPC NVRAM + nvram_set_string(nvram, 0x00, "QEMU_BIOS", 16); + nvram_set_lword(nvram, 0x10, 0x00000001); /* structure v1 */ + // NVRAM_size, arch not applicable + m48t59_write(nvram, 0x2D, smp_cpus & 0xff); + m48t59_write(nvram, 0x2E, 0); + m48t59_write(nvram, 0x2F, nographic & 0xff); + nvram_set_lword(nvram, 0x30, RAM_size); + m48t59_write(nvram, 0x34, boot_device & 0xff); + nvram_set_lword(nvram, 0x38, KERNEL_LOAD_ADDR); + nvram_set_lword(nvram, 0x3C, kernel_size); + if (cmdline) { + strcpy(phys_ram_base + CMDLINE_ADDR, cmdline); + nvram_set_lword(nvram, 0x40, CMDLINE_ADDR); + nvram_set_lword(nvram, 0x44, strlen(cmdline)); + } + // initrd_image, initrd_size passed differently + nvram_set_word(nvram, 0x54, width); + nvram_set_word(nvram, 0x56, height); + nvram_set_word(nvram, 0x58, depth); + + // Sun4m specific use + i = 0x1fd8; + m48t59_write(nvram, i++, 0x01); + m48t59_write(nvram, i++, 0x80); /* Sun4m OBP */ + j = 0; + m48t59_write(nvram, i++, macaddr[j++]); + m48t59_write(nvram, i++, macaddr[j++]); + m48t59_write(nvram, i++, macaddr[j++]); + m48t59_write(nvram, i++, macaddr[j++]); + m48t59_write(nvram, i++, macaddr[j++]); + m48t59_write(nvram, i, macaddr[j]); + + /* Calculate checksum */ + for (i = 0x1fd8; i < 0x1fe7; i++) { + tmp ^= m48t59_read(nvram, i); + } + m48t59_write(nvram, 0x1fe7, tmp); +} + +static void *slavio_intctl; + +void pic_info() +{ + slavio_pic_info(slavio_intctl); +} + +void irq_info() +{ + slavio_irq_info(slavio_intctl); +} + +void pic_set_irq(int irq, int level) +{ + slavio_pic_set_irq(slavio_intctl, irq, level); +} + +void pic_set_irq_cpu(int irq, int level, unsigned int cpu) +{ + slavio_pic_set_irq_cpu(slavio_intctl, irq, level, cpu); +} + +static void *iommu; + +uint32_t iommu_translate(uint32_t addr) +{ + return iommu_translate_local(iommu, addr); +} + +static void *slavio_misc; + +void qemu_system_powerdown(void) +{ + slavio_set_power_fail(slavio_misc, 1); +} + +static void main_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + cpu_reset(env); +} + +/* Sun4m hardware initialisation */ +static void sun4m_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + CPUState *env, *envs[MAX_CPUS]; + char buf[1024]; + int ret, linux_boot; + unsigned int i; + long vram_size = 0x100000, prom_offset, initrd_size, kernel_size; + + linux_boot = (kernel_filename != NULL); + + /* init CPUs */ + for(i = 0; i < smp_cpus; i++) { + env = cpu_init(); + envs[i] = env; + if (i != 0) + env->halted = 1; + register_savevm("cpu", i, 3, cpu_save, cpu_load, env); + qemu_register_reset(main_cpu_reset, env); + } + /* allocate RAM */ + cpu_register_physical_memory(0, ram_size, 0); + + iommu = iommu_init(PHYS_JJ_IOMMU); + slavio_intctl = slavio_intctl_init(PHYS_JJ_INTR0, PHYS_JJ_INTR_G); + for(i = 0; i < smp_cpus; i++) { + slavio_intctl_set_cpu(slavio_intctl, i, envs[i]); + } + + tcx_init(ds, PHYS_JJ_TCX_FB, phys_ram_base + ram_size, ram_size, vram_size, graphic_width, graphic_height); + if (nd_table[0].vlan) { + if (nd_table[0].model == NULL + || strcmp(nd_table[0].model, "lance") == 0) { + lance_init(&nd_table[0], PHYS_JJ_LE_IRQ, PHYS_JJ_LE, PHYS_JJ_LEDMA); + } else { + fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model); + exit (1); + } + } + nvram = m48t59_init(0, PHYS_JJ_EEPROM, 0, PHYS_JJ_EEPROM_SIZE, 8); + for (i = 0; i < MAX_CPUS; i++) { + slavio_timer_init(PHYS_JJ_CLOCK + i * TARGET_PAGE_SIZE, PHYS_JJ_CLOCK_IRQ, 0, i); + } + slavio_timer_init(PHYS_JJ_CLOCK1, PHYS_JJ_CLOCK1_IRQ, 2, (unsigned int)-1); + slavio_serial_ms_kbd_init(PHYS_JJ_MS_KBD, PHYS_JJ_MS_KBD_IRQ); + // Slavio TTYA (base+4, Linux ttyS0) is the first Qemu serial device + // Slavio TTYB (base+0, Linux ttyS1) is the second Qemu serial device + slavio_serial_init(PHYS_JJ_SER, PHYS_JJ_SER_IRQ, serial_hds[1], serial_hds[0]); + fdctrl_init(PHYS_JJ_FLOPPY_IRQ, 0, 1, PHYS_JJ_FDC, fd_table); + esp_init(bs_table, PHYS_JJ_ESP_IRQ, PHYS_JJ_ESP, PHYS_JJ_ESPDMA); + slavio_misc = slavio_misc_init(PHYS_JJ_SLAVIO, PHYS_JJ_ME_IRQ); + + prom_offset = ram_size + vram_size; + cpu_register_physical_memory(PROM_ADDR, + (PROM_SIZE_MAX + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK, + prom_offset | IO_MEM_ROM); + + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEE); + ret = load_elf(buf, 0, NULL); + if (ret < 0) { + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEB); + ret = load_image(buf, phys_ram_base + prom_offset); + } + if (ret < 0) { + fprintf(stderr, "qemu: could not load prom '%s'\n", + buf); + exit(1); + } + + kernel_size = 0; + if (linux_boot) { + kernel_size = load_elf(kernel_filename, -0xf0000000, NULL); + if (kernel_size < 0) + kernel_size = load_aout(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); + if (kernel_size < 0) + kernel_size = load_image(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + + /* load initrd */ + initrd_size = 0; + if (initrd_filename) { + initrd_size = load_image(initrd_filename, phys_ram_base + INITRD_LOAD_ADDR); + if (initrd_size < 0) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + } + if (initrd_size > 0) { + for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { + if (ldl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i) + == 0x48647253) { // HdrS + stl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i + 16, INITRD_LOAD_ADDR); + stl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i + 20, initrd_size); + break; + } + } + } + } + nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr, kernel_cmdline, boot_device, ram_size, kernel_size, graphic_width, graphic_height, graphic_depth); +} + +QEMUMachine sun4m_machine = { + "sun4m", + "Sun4m platform", + sun4m_init, +}; diff --git a/tools/ioemu/hw/sun4u.c b/tools/ioemu/hw/sun4u.c new file mode 100644 index 0000000000..208d3dd63a --- /dev/null +++ b/tools/ioemu/hw/sun4u.c @@ -0,0 +1,376 @@ +/* + * QEMU Sun4u System Emulator + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "m48t59.h" + +#define KERNEL_LOAD_ADDR 0x00404000 +#define CMDLINE_ADDR 0x003ff000 +#define INITRD_LOAD_ADDR 0x00300000 +#define PROM_SIZE_MAX (256 * 1024) +#define PROM_ADDR 0x1fff0000000ULL +#define APB_SPECIAL_BASE 0x1fe00000000ULL +#define APB_MEM_BASE 0x1ff00000000ULL +#define VGA_BASE (APB_MEM_BASE + 0x400000ULL) +#define PROM_FILENAMEB "proll-sparc64.bin" +#define PROM_FILENAMEE "proll-sparc64.elf" +#define NVRAM_SIZE 0x2000 + +/* TSC handling */ + +uint64_t cpu_get_tsc() +{ + return qemu_get_clock(vm_clock); +} + +int DMA_get_channel_mode (int nchan) +{ + return 0; +} +int DMA_read_memory (int nchan, void *buf, int pos, int size) +{ + return 0; +} +int DMA_write_memory (int nchan, void *buf, int pos, int size) +{ + return 0; +} +void DMA_hold_DREQ (int nchan) {} +void DMA_release_DREQ (int nchan) {} +void DMA_schedule(int nchan) {} +void DMA_run (void) {} +void DMA_init (int high_page_enable) {} +void DMA_register_channel (int nchan, + DMA_transfer_handler transfer_handler, + void *opaque) +{ +} + +/* NVRAM helpers */ +void NVRAM_set_byte (m48t59_t *nvram, uint32_t addr, uint8_t value) +{ + m48t59_write(nvram, addr, value); +} + +uint8_t NVRAM_get_byte (m48t59_t *nvram, uint32_t addr) +{ + return m48t59_read(nvram, addr); +} + +void NVRAM_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value) +{ + m48t59_write(nvram, addr, value >> 8); + m48t59_write(nvram, addr + 1, value & 0xFF); +} + +uint16_t NVRAM_get_word (m48t59_t *nvram, uint32_t addr) +{ + uint16_t tmp; + + tmp = m48t59_read(nvram, addr) << 8; + tmp |= m48t59_read(nvram, addr + 1); + + return tmp; +} + +void NVRAM_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value) +{ + m48t59_write(nvram, addr, value >> 24); + m48t59_write(nvram, addr + 1, (value >> 16) & 0xFF); + m48t59_write(nvram, addr + 2, (value >> 8) & 0xFF); + m48t59_write(nvram, addr + 3, value & 0xFF); +} + +uint32_t NVRAM_get_lword (m48t59_t *nvram, uint32_t addr) +{ + uint32_t tmp; + + tmp = m48t59_read(nvram, addr) << 24; + tmp |= m48t59_read(nvram, addr + 1) << 16; + tmp |= m48t59_read(nvram, addr + 2) << 8; + tmp |= m48t59_read(nvram, addr + 3); + + return tmp; +} + +void NVRAM_set_string (m48t59_t *nvram, uint32_t addr, + const unsigned char *str, uint32_t max) +{ + int i; + + for (i = 0; i < max && str[i] != '\0'; i++) { + m48t59_write(nvram, addr + i, str[i]); + } + m48t59_write(nvram, addr + max - 1, '\0'); +} + +int NVRAM_get_string (m48t59_t *nvram, uint8_t *dst, uint16_t addr, int max) +{ + int i; + + memset(dst, 0, max); + for (i = 0; i < max; i++) { + dst[i] = NVRAM_get_byte(nvram, addr + i); + if (dst[i] == '\0') + break; + } + + return i; +} + +static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value) +{ + uint16_t tmp; + uint16_t pd, pd1, pd2; + + tmp = prev >> 8; + pd = prev ^ value; + pd1 = pd & 0x000F; + pd2 = ((pd >> 4) & 0x000F) ^ pd1; + tmp ^= (pd1 << 3) | (pd1 << 8); + tmp ^= pd2 | (pd2 << 7) | (pd2 << 12); + + return tmp; +} + +uint16_t NVRAM_compute_crc (m48t59_t *nvram, uint32_t start, uint32_t count) +{ + uint32_t i; + uint16_t crc = 0xFFFF; + int odd; + + odd = count & 1; + count &= ~1; + for (i = 0; i != count; i++) { + crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i)); + } + if (odd) { + crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8); + } + + return crc; +} + +extern int nographic; + +int sun4u_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size, + const unsigned char *arch, + uint32_t RAM_size, int boot_device, + uint32_t kernel_image, uint32_t kernel_size, + const char *cmdline, + uint32_t initrd_image, uint32_t initrd_size, + uint32_t NVRAM_image, + int width, int height, int depth) +{ + uint16_t crc; + + /* Set parameters for Open Hack'Ware BIOS */ + NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16); + NVRAM_set_lword(nvram, 0x10, 0x00000002); /* structure v2 */ + NVRAM_set_word(nvram, 0x14, NVRAM_size); + NVRAM_set_string(nvram, 0x20, arch, 16); + NVRAM_set_byte(nvram, 0x2f, nographic & 0xff); + NVRAM_set_lword(nvram, 0x30, RAM_size); + NVRAM_set_byte(nvram, 0x34, boot_device); + NVRAM_set_lword(nvram, 0x38, kernel_image); + NVRAM_set_lword(nvram, 0x3C, kernel_size); + if (cmdline) { + /* XXX: put the cmdline in NVRAM too ? */ + strcpy(phys_ram_base + CMDLINE_ADDR, cmdline); + NVRAM_set_lword(nvram, 0x40, CMDLINE_ADDR); + NVRAM_set_lword(nvram, 0x44, strlen(cmdline)); + } else { + NVRAM_set_lword(nvram, 0x40, 0); + NVRAM_set_lword(nvram, 0x44, 0); + } + NVRAM_set_lword(nvram, 0x48, initrd_image); + NVRAM_set_lword(nvram, 0x4C, initrd_size); + NVRAM_set_lword(nvram, 0x50, NVRAM_image); + + NVRAM_set_word(nvram, 0x54, width); + NVRAM_set_word(nvram, 0x56, height); + NVRAM_set_word(nvram, 0x58, depth); + crc = NVRAM_compute_crc(nvram, 0x00, 0xF8); + NVRAM_set_word(nvram, 0xFC, crc); + + return 0; +} + +void pic_info() +{ +} + +void irq_info() +{ +} + +void pic_set_irq(int irq, int level) +{ +} + +void pic_set_irq_new(void *opaque, int irq, int level) +{ +} + +void qemu_system_powerdown(void) +{ +} + +static void main_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + cpu_reset(env); +} + +static const int ide_iobase[2] = { 0x1f0, 0x170 }; +static const int ide_iobase2[2] = { 0x3f6, 0x376 }; +static const int ide_irq[2] = { 14, 15 }; + +static const int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; +static const int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 }; + +static const int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc }; +static const int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 }; + +static fdctrl_t *floppy_controller; + +/* Sun4u hardware initialisation */ +static void sun4u_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + CPUState *env; + char buf[1024]; + m48t59_t *nvram; + int ret, linux_boot; + unsigned int i; + long prom_offset, initrd_size, kernel_size; + PCIBus *pci_bus; + + linux_boot = (kernel_filename != NULL); + + env = cpu_init(); + register_savevm("cpu", 0, 3, cpu_save, cpu_load, env); + qemu_register_reset(main_cpu_reset, env); + + /* allocate RAM */ + cpu_register_physical_memory(0, ram_size, 0); + + prom_offset = ram_size + vga_ram_size; + cpu_register_physical_memory(PROM_ADDR, + (PROM_SIZE_MAX + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK, + prom_offset | IO_MEM_ROM); + + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEE); + ret = load_elf(buf, 0, NULL); + if (ret < 0) { + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEB); + ret = load_image(buf, phys_ram_base + prom_offset); + } + if (ret < 0) { + fprintf(stderr, "qemu: could not load prom '%s'\n", + buf); + exit(1); + } + + kernel_size = 0; + initrd_size = 0; + if (linux_boot) { + /* XXX: put correct offset */ + kernel_size = load_elf(kernel_filename, 0, NULL); + if (kernel_size < 0) + kernel_size = load_aout(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); + if (kernel_size < 0) + kernel_size = load_image(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + + /* load initrd */ + if (initrd_filename) { + initrd_size = load_image(initrd_filename, phys_ram_base + INITRD_LOAD_ADDR); + if (initrd_size < 0) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + } + if (initrd_size > 0) { + for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { + if (ldl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i) + == 0x48647253) { // HdrS + stl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i + 16, INITRD_LOAD_ADDR); + stl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i + 20, initrd_size); + break; + } + } + } + } + pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE); + isa_mem_base = VGA_BASE; + vga_initialize(pci_bus, ds, phys_ram_base + ram_size, ram_size, + vga_ram_size, 0, 0); + cpu_register_physical_memory(VGA_BASE, vga_ram_size, ram_size); + //pci_cirrus_vga_init(pci_bus, ds, phys_ram_base + ram_size, ram_size, vga_ram_size); + + for(i = 0; i < MAX_SERIAL_PORTS; i++) { + if (serial_hds[i]) { + serial_init(&pic_set_irq_new, NULL, + serial_io[i], serial_irq[i], serial_hds[i]); + } + } + + for(i = 0; i < MAX_PARALLEL_PORTS; i++) { + if (parallel_hds[i]) { + parallel_init(parallel_io[i], parallel_irq[i], parallel_hds[i]); + } + } + + for(i = 0; i < nb_nics; i++) { + if (!nd_table[i].model) + nd_table[i].model = "ne2k_pci"; + pci_nic_init(pci_bus, &nd_table[i]); + } + + pci_cmd646_ide_init(pci_bus, bs_table, 1); + kbd_init(); + floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table); + nvram = m48t59_init(8, 0, 0x0074, NVRAM_SIZE, 59); + sun4u_NVRAM_set_params(nvram, NVRAM_SIZE, "Sun4u", ram_size, boot_device, + KERNEL_LOAD_ADDR, kernel_size, + kernel_cmdline, + INITRD_LOAD_ADDR, initrd_size, + /* XXX: need an option to load a NVRAM image */ + 0, + graphic_width, graphic_height, graphic_depth); + +} + +QEMUMachine sun4u_machine = { + "sun4u", + "Sun4u platform", + sun4u_init, +}; diff --git a/tools/ioemu/hw/tc58128.c b/tools/ioemu/hw/tc58128.c new file mode 100644 index 0000000000..a8b26f7e8c --- /dev/null +++ b/tools/ioemu/hw/tc58128.c @@ -0,0 +1,181 @@ +#include +#include "vl.h" + +#define CE1 0x0100 +#define CE2 0x0200 +#define RE 0x0400 +#define WE 0x0800 +#define ALE 0x1000 +#define CLE 0x2000 +#define RDY1 0x4000 +#define RDY2 0x8000 +#define RDY(n) ((n) == 0 ? RDY1 : RDY2) + +typedef enum { WAIT, READ1, READ2, READ3 } state_t; + +typedef struct { + uint8_t *flash_contents; + state_t state; + uint32_t address; + uint8_t address_cycle; +} tc58128_dev; + +static tc58128_dev tc58128_devs[2]; + +#define FLASH_SIZE (16*1024*1024) + +void init_dev(tc58128_dev * dev, char *filename) +{ + int ret, blocks; + + dev->state = WAIT; + dev->flash_contents = qemu_mallocz(FLASH_SIZE); + memset(dev->flash_contents, 0xff, FLASH_SIZE); + if (!dev->flash_contents) { + fprintf(stderr, "could not alloc memory for flash\n"); + exit(1); + } + if (filename) { + /* Load flash image skipping the first block */ + ret = load_image(filename, dev->flash_contents + 528 * 32); + if (ret < 0) { + fprintf(stderr, "ret=%d\n", ret); + fprintf(stderr, "qemu: could not load flash image %s\n", + filename); + exit(1); + } else { + /* Build first block with number of blocks */ + blocks = (ret + 528 * 32 - 1) / (528 * 32); + dev->flash_contents[0] = blocks & 0xff; + dev->flash_contents[1] = (blocks >> 8) & 0xff; + dev->flash_contents[2] = (blocks >> 16) & 0xff; + dev->flash_contents[3] = (blocks >> 24) & 0xff; + fprintf(stderr, "loaded %d bytes for %s into flash\n", ret, + filename); + } + } +} + +void handle_command(tc58128_dev * dev, uint8_t command) +{ + switch (command) { + case 0xff: + fprintf(stderr, "reset flash device\n"); + dev->state = WAIT; + break; + case 0x00: + fprintf(stderr, "read mode 1\n"); + dev->state = READ1; + dev->address_cycle = 0; + break; + case 0x01: + fprintf(stderr, "read mode 2\n"); + dev->state = READ2; + dev->address_cycle = 0; + break; + case 0x50: + fprintf(stderr, "read mode 3\n"); + dev->state = READ3; + dev->address_cycle = 0; + break; + default: + fprintf(stderr, "unknown flash command 0x%02x\n", command); + assert(0); + } +} + +void handle_address(tc58128_dev * dev, uint8_t data) +{ + switch (dev->state) { + case READ1: + case READ2: + case READ3: + switch (dev->address_cycle) { + case 0: + dev->address = data; + if (dev->state == READ2) + dev->address |= 0x100; + else if (dev->state == READ3) + dev->address |= 0x200; + break; + case 1: + dev->address += data * 528 * 0x100; + break; + case 2: + dev->address += data * 528; + fprintf(stderr, "address pointer in flash: 0x%08x\n", + dev->address); + break; + default: + /* Invalid data */ + assert(0); + } + dev->address_cycle++; + break; + default: + assert(0); + } +} + +uint8_t handle_read(tc58128_dev * dev) +{ +#if 0 + if (dev->address % 0x100000 == 0) + fprintf(stderr, "reading flash at address 0x%08x\n", dev->address); +#endif + return dev->flash_contents[dev->address++]; +} + +/* We never mark the device as busy, so interrupts cannot be triggered + XXXXX */ + +int tc58128_cb(uint16_t porta, uint16_t portb, + uint16_t * periph_pdtra, uint16_t * periph_portadir, + uint16_t * periph_pdtrb, uint16_t * periph_portbdir) +{ + int dev; + + if ((porta & CE1) == 0) + dev = 0; + else if ((porta & CE2) == 0) + dev = 1; + else + return 0; /* No device selected */ + + if ((porta & RE) && (porta & WE)) { + /* Nothing to do, assert ready and return to input state */ + *periph_portadir &= 0xff00; + *periph_portadir |= RDY(dev); + *periph_pdtra |= RDY(dev); + return 1; + } + + if (porta & CLE) { + /* Command */ + assert((porta & WE) == 0); + handle_command(&tc58128_devs[dev], porta & 0x00ff); + } else if (porta & ALE) { + assert((porta & WE) == 0); + handle_address(&tc58128_devs[dev], porta & 0x00ff); + } else if ((porta & RE) == 0) { + *periph_portadir |= 0x00ff; + *periph_pdtra &= 0xff00; + *periph_pdtra |= handle_read(&tc58128_devs[dev]); + } else { + assert(0); + } + return 1; +} + +static sh7750_io_device tc58128 = { + RE | WE, /* Port A triggers */ + 0, /* Port B triggers */ + tc58128_cb /* Callback */ +}; + +int tc58128_init(struct SH7750State *s, char *zone1, char *zone2) +{ + init_dev(&tc58128_devs[0], zone1); + init_dev(&tc58128_devs[1], zone2); + return sh7750_register_io_device(s, &tc58128); +} diff --git a/tools/ioemu/hw/tcx.c b/tools/ioemu/hw/tcx.c new file mode 100644 index 0000000000..a3a2114e8f --- /dev/null +++ b/tools/ioemu/hw/tcx.c @@ -0,0 +1,330 @@ +/* + * QEMU TCX Frame buffer + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#define MAXX 1024 +#define MAXY 768 +#define TCX_DAC_NREGS 16 + +typedef struct TCXState { + uint32_t addr; + DisplayState *ds; + uint8_t *vram; + unsigned long vram_offset; + uint16_t width, height; + uint8_t r[256], g[256], b[256]; + uint8_t dac_index, dac_state; +} TCXState; + +static void tcx_screen_dump(void *opaque, const char *filename); + +static void tcx_draw_line32(TCXState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + int x; + uint8_t val; + + for(x = 0; x < width; x++) { + val = *s++; + *d++ = s1->b[val]; + *d++ = s1->g[val]; + *d++ = s1->r[val]; + d++; + } +} + +static void tcx_draw_line24(TCXState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + int x; + uint8_t val; + + for(x = 0; x < width; x++) { + val = *s++; + *d++ = s1->b[val]; + *d++ = s1->g[val]; + *d++ = s1->r[val]; + } +} + +static void tcx_draw_line8(TCXState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + int x; + uint8_t val; + + for(x = 0; x < width; x++) { + val = *s++; + /* XXX translate between palettes? */ + *d++ = val; + } +} + +/* Fixed line length 1024 allows us to do nice tricks not possible on + VGA... */ +static void tcx_update_display(void *opaque) +{ + TCXState *ts = opaque; + uint32_t page; + int y, page_min, page_max, y_start, dd, ds; + uint8_t *d, *s; + void (*f)(TCXState *s1, uint8_t *d, const uint8_t *s, int width); + + if (ts->ds->depth == 0) + return; + page = ts->vram_offset; + y_start = -1; + page_min = 0x7fffffff; + page_max = -1; + d = ts->ds->data; + s = ts->vram; + dd = ts->ds->linesize; + ds = 1024; + + switch (ts->ds->depth) { + case 32: + f = tcx_draw_line32; + break; + case 24: + f = tcx_draw_line24; + break; + default: + case 8: + f = tcx_draw_line8; + break; + case 0: + return; + } + + for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) { + if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) { + if (y_start < 0) + y_start = y; + if (page < page_min) + page_min = page; + if (page > page_max) + page_max = page; + f(ts, d, s, ts->width); + d += dd; + s += ds; + f(ts, d, s, ts->width); + d += dd; + s += ds; + f(ts, d, s, ts->width); + d += dd; + s += ds; + f(ts, d, s, ts->width); + d += dd; + s += ds; + } else { + if (y_start >= 0) { + /* flush to display */ + dpy_update(ts->ds, 0, y_start, + ts->width, y - y_start); + y_start = -1; + } + d += dd * 4; + s += ds * 4; + } + } + if (y_start >= 0) { + /* flush to display */ + dpy_update(ts->ds, 0, y_start, + ts->width, y - y_start); + } + /* reset modified pages */ + if (page_max != -1) { + cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE, + VGA_DIRTY_FLAG); + } +} + +static void tcx_invalidate_display(void *opaque) +{ + TCXState *s = opaque; + int i; + + for (i = 0; i < MAXX*MAXY; i += TARGET_PAGE_SIZE) { + cpu_physical_memory_set_dirty(s->vram_offset + i); + } +} + +static void tcx_save(QEMUFile *f, void *opaque) +{ + TCXState *s = opaque; + + qemu_put_be32s(f, (uint32_t *)&s->addr); + qemu_put_be32s(f, (uint32_t *)&s->vram); + qemu_put_be16s(f, (uint16_t *)&s->height); + qemu_put_be16s(f, (uint16_t *)&s->width); + qemu_put_buffer(f, s->r, 256); + qemu_put_buffer(f, s->g, 256); + qemu_put_buffer(f, s->b, 256); + qemu_put_8s(f, &s->dac_index); + qemu_put_8s(f, &s->dac_state); +} + +static int tcx_load(QEMUFile *f, void *opaque, int version_id) +{ + TCXState *s = opaque; + + if (version_id != 1) + return -EINVAL; + + qemu_get_be32s(f, (uint32_t *)&s->addr); + qemu_get_be32s(f, (uint32_t *)&s->vram); + qemu_get_be16s(f, (uint16_t *)&s->height); + qemu_get_be16s(f, (uint16_t *)&s->width); + qemu_get_buffer(f, s->r, 256); + qemu_get_buffer(f, s->g, 256); + qemu_get_buffer(f, s->b, 256); + qemu_get_8s(f, &s->dac_index); + qemu_get_8s(f, &s->dac_state); + return 0; +} + +static void tcx_reset(void *opaque) +{ + TCXState *s = opaque; + + /* Initialize palette */ + memset(s->r, 0, 256); + memset(s->g, 0, 256); + memset(s->b, 0, 256); + s->r[255] = s->g[255] = s->b[255] = 255; + memset(s->vram, 0, MAXX*MAXY); + cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset + MAXX*MAXY, + VGA_DIRTY_FLAG); + s->dac_index = 0; + s->dac_state = 0; +} + +static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr) +{ + return 0; +} + +static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + TCXState *s = opaque; + uint32_t saddr; + + saddr = (addr & (TCX_DAC_NREGS - 1)) >> 2; + switch (saddr) { + case 0: + s->dac_index = val >> 24; + s->dac_state = 0; + break; + case 1: + switch (s->dac_state) { + case 0: + s->r[s->dac_index] = val >> 24; + s->dac_state++; + break; + case 1: + s->g[s->dac_index] = val >> 24; + s->dac_state++; + break; + case 2: + s->b[s->dac_index] = val >> 24; + default: + s->dac_state = 0; + break; + } + break; + default: + break; + } + return; +} + +static CPUReadMemoryFunc *tcx_dac_read[3] = { + tcx_dac_readl, + tcx_dac_readl, + tcx_dac_readl, +}; + +static CPUWriteMemoryFunc *tcx_dac_write[3] = { + tcx_dac_writel, + tcx_dac_writel, + tcx_dac_writel, +}; + +void tcx_init(DisplayState *ds, uint32_t addr, uint8_t *vram_base, + unsigned long vram_offset, int vram_size, int width, int height) +{ + TCXState *s; + int io_memory; + + s = qemu_mallocz(sizeof(TCXState)); + if (!s) + return; + s->ds = ds; + s->addr = addr; + s->vram = vram_base; + s->vram_offset = vram_offset; + s->width = width; + s->height = height; + + cpu_register_physical_memory(addr + 0x800000, vram_size, vram_offset); + io_memory = cpu_register_io_memory(0, tcx_dac_read, tcx_dac_write, s); + cpu_register_physical_memory(addr + 0x200000, TCX_DAC_NREGS, io_memory); + + graphic_console_init(s->ds, tcx_update_display, tcx_invalidate_display, + tcx_screen_dump, s); + register_savevm("tcx", addr, 1, tcx_save, tcx_load, s); + qemu_register_reset(tcx_reset, s); + tcx_reset(s); + dpy_resize(s->ds, width, height); +} + +static void tcx_screen_dump(void *opaque, const char *filename) +{ + TCXState *s = opaque; + FILE *f; + uint8_t *d, *d1, v; + int y, x; + + f = fopen(filename, "wb"); + if (!f) + return; + fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255); + d1 = s->vram; + for(y = 0; y < s->height; y++) { + d = d1; + for(x = 0; x < s->width; x++) { + v = *d; + fputc(s->r[v], f); + fputc(s->g[v], f); + fputc(s->b[v], f); + d++; + } + d1 += MAXX; + } + fclose(f); + return; +} + + + diff --git a/tools/ioemu/hw/usb-hid.c b/tools/ioemu/hw/usb-hid.c new file mode 100644 index 0000000000..17160ebe32 --- /dev/null +++ b/tools/ioemu/hw/usb-hid.c @@ -0,0 +1,537 @@ +/* + * QEMU USB HID devices + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* HID interface requests */ +#define GET_REPORT 0xa101 +#define GET_IDLE 0xa102 +#define GET_PROTOCOL 0xa103 +#define SET_IDLE 0x210a +#define SET_PROTOCOL 0x210b + +#define USB_MOUSE 1 +#define USB_TABLET 2 + +typedef struct USBMouseState { + USBDevice dev; + int dx, dy, dz, buttons_state; + int x, y; + int kind; + int mouse_grabbed; +} USBMouseState; + +/* mostly the same values as the Bochs USB Mouse device */ +static const uint8_t qemu_mouse_dev_descriptor[] = { + 0x12, /* u8 bLength; */ + 0x01, /* u8 bDescriptorType; Device */ + 0x10, 0x00, /* u16 bcdUSB; v1.0 */ + + 0x00, /* u8 bDeviceClass; */ + 0x00, /* u8 bDeviceSubClass; */ + 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ + 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ + + 0x27, 0x06, /* u16 idVendor; */ + 0x01, 0x00, /* u16 idProduct; */ + 0x00, 0x00, /* u16 bcdDevice */ + + 0x03, /* u8 iManufacturer; */ + 0x02, /* u8 iProduct; */ + 0x01, /* u8 iSerialNumber; */ + 0x01 /* u8 bNumConfigurations; */ +}; + +static const uint8_t qemu_mouse_config_descriptor[] = { + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x22, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 50, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x03, /* u8 if_bInterfaceClass; */ + 0x01, /* u8 if_bInterfaceSubClass; */ + 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x05, /* u8 if_iInterface; */ + + /* HID descriptor */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 50, 0, /* u16 len */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x03, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_tablet_config_descriptor[] = { + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x22, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 50, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x03, /* u8 if_bInterfaceClass; */ + 0x01, /* u8 if_bInterfaceSubClass; */ + 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x05, /* u8 if_iInterface; */ + + /* HID descriptor */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 74, 0, /* u16 len */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x03, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_mouse_hid_report_descriptor[] = { + 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, + 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, + 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, + 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, + 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81, + 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06, + 0xC0, 0xC0, +}; + +static const uint8_t qemu_tablet_hid_report_descriptor[] = { + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x01, /* Usage Mouse */ + 0xA1, 0x01, /* Collection Application */ + 0x09, 0x01, /* Usage Pointer */ + 0xA1, 0x00, /* Collection Physical */ + 0x05, 0x09, /* Usage Page Button */ + 0x19, 0x01, /* Usage Minimum Button 1 */ + 0x29, 0x03, /* Usage Maximum Button 3 */ + 0x15, 0x00, /* Logical Minimum 0 */ + 0x25, 0x01, /* Logical Maximum 1 */ + 0x95, 0x03, /* Report Count 3 */ + 0x75, 0x01, /* Report Size 1 */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x95, 0x01, /* Report Count 1 */ + 0x75, 0x05, /* Report Size 5 */ + 0x81, 0x01, /* Input (Cnst, Var, Abs) */ + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x30, /* Usage X */ + 0x09, 0x31, /* Usage Y */ + 0x15, 0x00, /* Logical Minimum 0 */ + 0x26, 0xFF, 0x7F, /* Logical Maximum 0x7fff */ + 0x35, 0x00, /* Physical Minimum 0 */ + 0x46, 0xFE, 0x7F, /* Physical Maximum 0x7fff */ + 0x75, 0x10, /* Report Size 16 */ + 0x95, 0x02, /* Report Count 2 */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x38, /* Usage Wheel */ + 0x15, 0x81, /* Logical Minimum -127 */ + 0x25, 0x7F, /* Logical Maximum 127 */ + 0x35, 0x00, /* Physical Minimum 0 (same as logical) */ + 0x45, 0x00, /* Physical Maximum 0 (same as logical) */ + 0x75, 0x08, /* Report Size 8 */ + 0x95, 0x01, /* Report Count 1 */ + 0x81, 0x02, /* Input (Data, Var, Rel) */ + 0xC0, /* End Collection */ + 0xC0, /* End Collection */ +}; + +static void usb_mouse_event(void *opaque, + int dx1, int dy1, int dz1, int buttons_state) +{ + USBMouseState *s = opaque; + + s->dx += dx1; + s->dy += dy1; + s->dz += dz1; + s->buttons_state = buttons_state; +} + +static void usb_tablet_event(void *opaque, + int x, int y, int dz, int buttons_state) +{ + USBMouseState *s = opaque; + + s->x = x; + s->y = y; + s->dz += dz; + s->buttons_state = buttons_state; +} + +static inline int int_clamp(int val, int vmin, int vmax) +{ + if (val < vmin) + return vmin; + else if (val > vmax) + return vmax; + else + return val; +} + +static int usb_mouse_poll(USBMouseState *s, uint8_t *buf, int len) +{ + int dx, dy, dz, b, l; + + if (!s->mouse_grabbed) { + qemu_add_mouse_event_handler(usb_mouse_event, s, 0); + s->mouse_grabbed = 1; + } + + dx = int_clamp(s->dx, -128, 127); + dy = int_clamp(s->dy, -128, 127); + dz = int_clamp(s->dz, -128, 127); + + s->dx -= dx; + s->dy -= dy; + s->dz -= dz; + + b = 0; + if (s->buttons_state & MOUSE_EVENT_LBUTTON) + b |= 0x01; + if (s->buttons_state & MOUSE_EVENT_RBUTTON) + b |= 0x02; + if (s->buttons_state & MOUSE_EVENT_MBUTTON) + b |= 0x04; + + buf[0] = b; + buf[1] = dx; + buf[2] = dy; + l = 3; + if (len >= 4) { + buf[3] = dz; + l = 4; + } + return l; +} + +static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len) +{ + int dz, b, l; + + if (!s->mouse_grabbed) { + qemu_add_mouse_event_handler(usb_tablet_event, s, 1); + s->mouse_grabbed = 1; + } + + dz = int_clamp(s->dz, -128, 127); + s->dz -= dz; + + /* Appears we have to invert the wheel direction */ + dz = 0 - dz; + b = 0; + if (s->buttons_state & MOUSE_EVENT_LBUTTON) + b |= 0x01; + if (s->buttons_state & MOUSE_EVENT_RBUTTON) + b |= 0x02; + if (s->buttons_state & MOUSE_EVENT_MBUTTON) + b |= 0x04; + + buf[0] = b; + buf[1] = s->x & 0xff; + buf[2] = s->x >> 8; + buf[3] = s->y & 0xff; + buf[4] = s->y >> 8; + buf[5] = dz; + l = 6; + + return l; +} + +static void usb_mouse_handle_reset(USBDevice *dev) +{ + USBMouseState *s = (USBMouseState *)dev; + + s->dx = 0; + s->dy = 0; + s->dz = 0; + s->x = 0; + s->y = 0; + s->buttons_state = 0; +} + +static int usb_mouse_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + USBMouseState *s = (USBMouseState *)dev; + int ret = 0; + + switch(request) { + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = (1 << USB_DEVICE_SELF_POWERED) | + (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case USB_DT_DEVICE: + memcpy(data, qemu_mouse_dev_descriptor, + sizeof(qemu_mouse_dev_descriptor)); + ret = sizeof(qemu_mouse_dev_descriptor); + break; + case USB_DT_CONFIG: + if (s->kind == USB_MOUSE) { + memcpy(data, qemu_mouse_config_descriptor, + sizeof(qemu_mouse_config_descriptor)); + ret = sizeof(qemu_mouse_config_descriptor); + } else if (s->kind == USB_TABLET) { + memcpy(data, qemu_tablet_config_descriptor, + sizeof(qemu_tablet_config_descriptor)); + ret = sizeof(qemu_tablet_config_descriptor); + } + break; + case USB_DT_STRING: + switch(value & 0xff) { + case 0: + /* language ids */ + data[0] = 4; + data[1] = 3; + data[2] = 0x09; + data[3] = 0x04; + ret = 4; + break; + case 1: + /* serial number */ + ret = set_usb_string(data, "1"); + break; + case 2: + /* product description */ + if (s->kind == USB_MOUSE) + ret = set_usb_string(data, "QEMU USB Mouse"); + else if (s->kind == USB_TABLET) + ret = set_usb_string(data, "QEMU USB Tablet"); + break; + case 3: + /* vendor description */ + ret = set_usb_string(data, "QEMU " QEMU_VERSION); + break; + case 4: + ret = set_usb_string(data, "HID Mouse"); + break; + case 5: + ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); + break; + default: + goto fail; + } + break; + default: + goto fail; + } + break; + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = 1; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_INTERFACE: + data[0] = 0; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_INTERFACE: + ret = 0; + break; + /* hid specific requests */ + case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case 0x22: + if (s->kind == USB_MOUSE) { + memcpy(data, qemu_mouse_hid_report_descriptor, + sizeof(qemu_mouse_hid_report_descriptor)); + ret = sizeof(qemu_mouse_hid_report_descriptor); + } else if (s->kind == USB_TABLET) { + memcpy(data, qemu_tablet_hid_report_descriptor, + sizeof(qemu_tablet_hid_report_descriptor)); + ret = sizeof(qemu_tablet_hid_report_descriptor); + } + break; + default: + goto fail; + } + break; + case GET_REPORT: + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, length); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, length); + break; + case SET_IDLE: + ret = 0; + break; + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int usb_mouse_handle_data(USBDevice *dev, int pid, + uint8_t devep, uint8_t *data, int len) +{ + USBMouseState *s = (USBMouseState *)dev; + int ret = 0; + + switch(pid) { + case USB_TOKEN_IN: + if (devep == 1) { + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, len); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, len); + } else { + goto fail; + } + break; + case USB_TOKEN_OUT: + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +USBDevice *usb_tablet_init(void) +{ + USBMouseState *s; + + s = qemu_mallocz(sizeof(USBMouseState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_generic_handle_packet; + + s->dev.handle_reset = usb_mouse_handle_reset; + s->dev.handle_control = usb_mouse_handle_control; + s->dev.handle_data = usb_mouse_handle_data; + s->kind = USB_TABLET; + + return (USBDevice *)s; +} + +USBDevice *usb_mouse_init(void) +{ + USBMouseState *s; + + s = qemu_mallocz(sizeof(USBMouseState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_generic_handle_packet; + + s->dev.handle_reset = usb_mouse_handle_reset; + s->dev.handle_control = usb_mouse_handle_control; + s->dev.handle_data = usb_mouse_handle_data; + s->kind = USB_MOUSE; + + return (USBDevice *)s; +} diff --git a/tools/ioemu/hw/usb-hub.c b/tools/ioemu/hw/usb-hub.c new file mode 100644 index 0000000000..fd916128cf --- /dev/null +++ b/tools/ioemu/hw/usb-hub.c @@ -0,0 +1,549 @@ +/* + * QEMU USB HUB emulation + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG + +#define MAX_PORTS 8 + +typedef struct USBHubPort { + USBPort port; + uint16_t wPortStatus; + uint16_t wPortChange; +} USBHubPort; + +typedef struct USBHubState { + USBDevice dev; + int nb_ports; + USBHubPort ports[MAX_PORTS]; +} USBHubState; + +#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) +#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE) +#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR) +#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS) +#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS) +#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE) +#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE) + +#define PORT_STAT_CONNECTION 0x0001 +#define PORT_STAT_ENABLE 0x0002 +#define PORT_STAT_SUSPEND 0x0004 +#define PORT_STAT_OVERCURRENT 0x0008 +#define PORT_STAT_RESET 0x0010 +#define PORT_STAT_POWER 0x0100 +#define PORT_STAT_LOW_SPEED 0x0200 +#define PORT_STAT_HIGH_SPEED 0x0400 +#define PORT_STAT_TEST 0x0800 +#define PORT_STAT_INDICATOR 0x1000 + +#define PORT_STAT_C_CONNECTION 0x0001 +#define PORT_STAT_C_ENABLE 0x0002 +#define PORT_STAT_C_SUSPEND 0x0004 +#define PORT_STAT_C_OVERCURRENT 0x0008 +#define PORT_STAT_C_RESET 0x0010 + +#define PORT_CONNECTION 0 +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVERCURRENT 3 +#define PORT_RESET 4 +#define PORT_POWER 8 +#define PORT_LOWSPEED 9 +#define PORT_HIGHSPEED 10 +#define PORT_C_CONNECTION 16 +#define PORT_C_ENABLE 17 +#define PORT_C_SUSPEND 18 +#define PORT_C_OVERCURRENT 19 +#define PORT_C_RESET 20 +#define PORT_TEST 21 +#define PORT_INDICATOR 22 + +/* same as Linux kernel root hubs */ + +static const uint8_t qemu_hub_dev_descriptor[] = { + 0x12, /* u8 bLength; */ + 0x01, /* u8 bDescriptorType; Device */ + 0x10, 0x01, /* u16 bcdUSB; v1.1 */ + + 0x09, /* u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* u8 bDeviceSubClass; */ + 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ + 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ + + 0x00, 0x00, /* u16 idVendor; */ + 0x00, 0x00, /* u16 idProduct; */ + 0x01, 0x01, /* u16 bcdDevice */ + + 0x03, /* u8 iManufacturer; */ + 0x02, /* u8 iProduct; */ + 0x01, /* u8 iSerialNumber; */ + 0x01 /* u8 bNumConfigurations; */ +}; + +/* XXX: patch interrupt size */ +static const uint8_t qemu_hub_config_descriptor[] = { + + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x19, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x00, /* u8 iConfiguration; */ + 0xc0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 0x00, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* u8 if_bInterfaceSubClass; */ + 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x00, /* u8 if_iInterface; */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ + 0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_hub_hub_descriptor[] = +{ + 0x09, /* u8 bLength; */ + 0x29, /* u8 bDescriptorType; Hub-descriptor */ + 0x00, /* u8 bNbrPorts; (patched later) */ + 0x0a, /* u16 wHubCharacteristics; */ + 0x00, /* (per-port OC, no power switching) */ + 0x01, /* u8 bPwrOn2pwrGood; 2ms */ + 0x00 /* u8 bHubContrCurrent; 0 mA */ + + /* DeviceRemovable and PortPwrCtrlMask patched in later */ +}; + +static void usb_hub_attach(USBPort *port1, USBDevice *dev) +{ + USBHubState *s = port1->opaque; + USBHubPort *port = &s->ports[port1->index]; + + if (dev) { + if (port->port.dev) + usb_attach(port1, NULL); + + port->wPortStatus |= PORT_STAT_CONNECTION; + port->wPortChange |= PORT_STAT_C_CONNECTION; + if (dev->speed == USB_SPEED_LOW) + port->wPortStatus |= PORT_STAT_LOW_SPEED; + else + port->wPortStatus &= ~PORT_STAT_LOW_SPEED; + port->port.dev = dev; + } else { + dev = port->port.dev; + if (dev) { + port->wPortStatus &= ~PORT_STAT_CONNECTION; + port->wPortChange |= PORT_STAT_C_CONNECTION; + if (port->wPortStatus & PORT_STAT_ENABLE) { + port->wPortStatus &= ~PORT_STAT_ENABLE; + port->wPortChange |= PORT_STAT_C_ENABLE; + } + port->port.dev = NULL; + } + } +} + +static void usb_hub_handle_reset(USBDevice *dev) +{ + /* XXX: do it */ +} + +static int usb_hub_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + USBHubState *s = (USBHubState *)dev; + int ret; + + switch(request) { + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = (1 << USB_DEVICE_SELF_POWERED) | + (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + } else { + goto fail; + } + ret = 0; + break; + case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == 0 && index != 0x81) { /* clear ep halt */ + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + } else { + goto fail; + } + ret = 0; + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + switch(value >> 8) { + case USB_DT_DEVICE: + memcpy(data, qemu_hub_dev_descriptor, + sizeof(qemu_hub_dev_descriptor)); + ret = sizeof(qemu_hub_dev_descriptor); + break; + case USB_DT_CONFIG: + memcpy(data, qemu_hub_config_descriptor, + sizeof(qemu_hub_config_descriptor)); + + /* status change endpoint size based on number + * of ports */ + data[22] = (s->nb_ports + 1 + 7) / 8; + + ret = sizeof(qemu_hub_config_descriptor); + break; + case USB_DT_STRING: + switch(value & 0xff) { + case 0: + /* language ids */ + data[0] = 4; + data[1] = 3; + data[2] = 0x09; + data[3] = 0x04; + ret = 4; + break; + case 1: + /* serial number */ + ret = set_usb_string(data, "314159"); + break; + case 2: + /* product description */ + ret = set_usb_string(data, "QEMU USB Hub"); + break; + case 3: + /* vendor description */ + ret = set_usb_string(data, "QEMU " QEMU_VERSION); + break; + default: + goto fail; + } + break; + default: + goto fail; + } + break; + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = 1; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + ret = 0; + break; + case DeviceRequest | USB_REQ_GET_INTERFACE: + data[0] = 0; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_INTERFACE: + ret = 0; + break; + /* usb specific requests */ + case GetHubStatus: + data[0] = 0; + data[1] = 0; + data[2] = 0; + data[3] = 0; + ret = 4; + break; + case GetPortStatus: + { + unsigned int n = index - 1; + USBHubPort *port; + if (n >= s->nb_ports) + goto fail; + port = &s->ports[n]; + data[0] = port->wPortStatus; + data[1] = port->wPortStatus >> 8; + data[2] = port->wPortChange; + data[3] = port->wPortChange >> 8; + ret = 4; + } + break; + case SetHubFeature: + case ClearHubFeature: + if (value == 0 || value == 1) { + } else { + goto fail; + } + ret = 0; + break; + case SetPortFeature: + { + unsigned int n = index - 1; + USBHubPort *port; + USBDevice *dev; + if (n >= s->nb_ports) + goto fail; + port = &s->ports[n]; + dev = port->port.dev; + switch(value) { + case PORT_SUSPEND: + port->wPortStatus |= PORT_STAT_SUSPEND; + break; + case PORT_RESET: + if (dev) { + dev->handle_packet(dev, + USB_MSG_RESET, 0, 0, NULL, 0); + port->wPortChange |= PORT_STAT_C_RESET; + /* set enable bit */ + port->wPortStatus |= PORT_STAT_ENABLE; + } + break; + case PORT_POWER: + break; + default: + goto fail; + } + ret = 0; + } + break; + case ClearPortFeature: + { + unsigned int n = index - 1; + USBHubPort *port; + USBDevice *dev; + if (n >= s->nb_ports) + goto fail; + port = &s->ports[n]; + dev = port->port.dev; + switch(value) { + case PORT_ENABLE: + port->wPortStatus &= ~PORT_STAT_ENABLE; + break; + case PORT_C_ENABLE: + port->wPortChange &= ~PORT_STAT_C_ENABLE; + break; + case PORT_SUSPEND: + port->wPortStatus &= ~PORT_STAT_SUSPEND; + break; + case PORT_C_SUSPEND: + port->wPortChange &= ~PORT_STAT_C_SUSPEND; + break; + case PORT_C_CONNECTION: + port->wPortChange &= ~PORT_STAT_C_CONNECTION; + break; + case PORT_C_OVERCURRENT: + port->wPortChange &= ~PORT_STAT_C_OVERCURRENT; + break; + case PORT_C_RESET: + port->wPortChange &= ~PORT_STAT_C_RESET; + break; + default: + goto fail; + } + ret = 0; + } + break; + case GetHubDescriptor: + { + unsigned int n, limit, var_hub_size = 0; + memcpy(data, qemu_hub_hub_descriptor, + sizeof(qemu_hub_hub_descriptor)); + data[2] = s->nb_ports; + + /* fill DeviceRemovable bits */ + limit = ((s->nb_ports + 1 + 7) / 8) + 7; + for (n = 7; n < limit; n++) { + data[n] = 0x00; + var_hub_size++; + } + + /* fill PortPwrCtrlMask bits */ + limit = limit + ((s->nb_ports + 7) / 8); + for (;n < limit; n++) { + data[n] = 0xff; + var_hub_size++; + } + + ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size; + break; + } + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int usb_hub_handle_data(USBDevice *dev, int pid, + uint8_t devep, uint8_t *data, int len) +{ + USBHubState *s = (USBHubState *)dev; + int ret; + + switch(pid) { + case USB_TOKEN_IN: + if (devep == 1) { + USBHubPort *port; + unsigned int status; + int i, n; + n = (s->nb_ports + 1 + 7) / 8; + if (len == 1) { /* FreeBSD workaround */ + n = 1; + } else if (n > len) { + return USB_RET_BABBLE; + } + status = 0; + for(i = 0; i < s->nb_ports; i++) { + port = &s->ports[i]; + if (port->wPortChange) + status |= (1 << (i + 1)); + } + if (status != 0) { + for(i = 0; i < n; i++) { + data[i] = status >> (8 * i); + } + ret = n; + } else { + ret = USB_RET_NAK; /* usb11 11.13.1 */ + } + } else { + goto fail; + } + break; + case USB_TOKEN_OUT: + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +static int usb_hub_broadcast_packet(USBHubState *s, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len) +{ + USBHubPort *port; + USBDevice *dev; + int i, ret; + + for(i = 0; i < s->nb_ports; i++) { + port = &s->ports[i]; + dev = port->port.dev; + if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) { + ret = dev->handle_packet(dev, pid, + devaddr, devep, + data, len); + if (ret != USB_RET_NODEV) { + return ret; + } + } + } + return USB_RET_NODEV; +} + +static int usb_hub_handle_packet(USBDevice *dev, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len) +{ + USBHubState *s = (USBHubState *)dev; + +#if defined(DEBUG) && 0 + printf("usb_hub: pid=0x%x\n", pid); +#endif + if (dev->state == USB_STATE_DEFAULT && + dev->addr != 0 && + devaddr != dev->addr && + (pid == USB_TOKEN_SETUP || + pid == USB_TOKEN_OUT || + pid == USB_TOKEN_IN)) { + /* broadcast the packet to the devices */ + return usb_hub_broadcast_packet(s, pid, devaddr, devep, data, len); + } + return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len); +} + +USBDevice *usb_hub_init(USBPort **usb_ports, int nb_ports) +{ + USBHubState *s; + USBHubPort *port; + int i; + + if (nb_ports > MAX_PORTS) + return NULL; + s = qemu_mallocz(sizeof(USBHubState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_hub_handle_packet; + + /* generic USB device init */ + s->dev.handle_reset = usb_hub_handle_reset; + s->dev.handle_control = usb_hub_handle_control; + s->dev.handle_data = usb_hub_handle_data; + + s->nb_ports = nb_ports; + for(i = 0; i < s->nb_ports; i++) { + port = &s->ports[i]; + port->wPortStatus = PORT_STAT_POWER; + port->wPortChange = 0; + port->port.attach = usb_hub_attach; + port->port.opaque = s; + port->port.index = i; + usb_ports[i] = &port->port; + } + return (USBDevice *)s; +} diff --git a/tools/ioemu/hw/usb-uhci.c b/tools/ioemu/hw/usb-uhci.c new file mode 100644 index 0000000000..23964f36a5 --- /dev/null +++ b/tools/ioemu/hw/usb-uhci.c @@ -0,0 +1,680 @@ +/* + * USB UHCI controller emulation + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +//#define DEBUG +//#define DEBUG_PACKET + +#define UHCI_CMD_GRESET (1 << 2) +#define UHCI_CMD_HCRESET (1 << 1) +#define UHCI_CMD_RS (1 << 0) + +#define UHCI_STS_HCHALTED (1 << 5) +#define UHCI_STS_HCPERR (1 << 4) +#define UHCI_STS_HSERR (1 << 3) +#define UHCI_STS_RD (1 << 2) +#define UHCI_STS_USBERR (1 << 1) +#define UHCI_STS_USBINT (1 << 0) + +#define TD_CTRL_SPD (1 << 29) +#define TD_CTRL_ERROR_SHIFT 27 +#define TD_CTRL_IOS (1 << 25) +#define TD_CTRL_IOC (1 << 24) +#define TD_CTRL_ACTIVE (1 << 23) +#define TD_CTRL_STALL (1 << 22) +#define TD_CTRL_BABBLE (1 << 20) +#define TD_CTRL_NAK (1 << 19) +#define TD_CTRL_TIMEOUT (1 << 18) + +#define UHCI_PORT_RESET (1 << 9) +#define UHCI_PORT_LSDA (1 << 8) +#define UHCI_PORT_ENC (1 << 3) +#define UHCI_PORT_EN (1 << 2) +#define UHCI_PORT_CSC (1 << 1) +#define UHCI_PORT_CCS (1 << 0) + +#define FRAME_TIMER_FREQ 1000 + +#define FRAME_MAX_LOOPS 100 + +#define NB_PORTS 2 + +typedef struct UHCIPort { + USBPort port; + uint16_t ctrl; +} UHCIPort; + +typedef struct UHCIState { + PCIDevice dev; + uint16_t cmd; /* cmd register */ + uint16_t status; + uint16_t intr; /* interrupt enable register */ + uint16_t frnum; /* frame number */ + uint32_t fl_base_addr; /* frame list base address */ + uint8_t sof_timing; + uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */ + QEMUTimer *frame_timer; + UHCIPort ports[NB_PORTS]; +} UHCIState; + +typedef struct UHCI_TD { + uint32_t link; + uint32_t ctrl; /* see TD_CTRL_xxx */ + uint32_t token; + uint32_t buffer; +} UHCI_TD; + +typedef struct UHCI_QH { + uint32_t link; + uint32_t el_link; +} UHCI_QH; + +static void uhci_attach(USBPort *port1, USBDevice *dev); + +static void uhci_update_irq(UHCIState *s) +{ + int level; + if (((s->status2 & 1) && (s->intr & (1 << 2))) || + ((s->status2 & 2) && (s->intr & (1 << 3))) || + ((s->status & UHCI_STS_USBERR) && (s->intr & (1 << 0))) || + ((s->status & UHCI_STS_RD) && (s->intr & (1 << 1))) || + (s->status & UHCI_STS_HSERR) || + (s->status & UHCI_STS_HCPERR)) { + level = 1; + } else { + level = 0; + } + pci_set_irq(&s->dev, 3, level); +} + +static void uhci_reset(UHCIState *s) +{ + uint8_t *pci_conf; + int i; + UHCIPort *port; + + pci_conf = s->dev.config; + + pci_conf[0x6a] = 0x01; /* usb clock */ + pci_conf[0x6b] = 0x00; + s->cmd = 0; + s->status = 0; + s->status2 = 0; + s->intr = 0; + s->fl_base_addr = 0; + s->sof_timing = 64; + for(i = 0; i < NB_PORTS; i++) { + port = &s->ports[i]; + port->ctrl = 0x0080; + if (port->port.dev) + uhci_attach(&port->port, port->port.dev); + } +} + +static void uhci_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + UHCIState *s = opaque; + + addr &= 0x1f; + switch(addr) { + case 0x0c: + s->sof_timing = val; + break; + } +} + +static uint32_t uhci_ioport_readb(void *opaque, uint32_t addr) +{ + UHCIState *s = opaque; + uint32_t val; + + addr &= 0x1f; + switch(addr) { + case 0x0c: + val = s->sof_timing; + break; + default: + val = 0xff; + break; + } + return val; +} + +static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) +{ + UHCIState *s = opaque; + + addr &= 0x1f; +#ifdef DEBUG + printf("uhci writew port=0x%04x val=0x%04x\n", addr, val); +#endif + switch(addr) { + case 0x00: + if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) { + /* start frame processing */ + qemu_mod_timer(s->frame_timer, qemu_get_clock(vm_clock)); + s->status &= ~UHCI_STS_HCHALTED; + } else if (!(val & UHCI_CMD_RS)) { + s->status |= UHCI_STS_HCHALTED; + } + if (val & UHCI_CMD_GRESET) { + UHCIPort *port; + USBDevice *dev; + int i; + + /* send reset on the USB bus */ + for(i = 0; i < NB_PORTS; i++) { + port = &s->ports[i]; + dev = port->port.dev; + if (dev) { + dev->handle_packet(dev, + USB_MSG_RESET, 0, 0, NULL, 0); + } + } + uhci_reset(s); + return; + } + if (val & UHCI_CMD_HCRESET) { + uhci_reset(s); + return; + } + s->cmd = val; + break; + case 0x02: + s->status &= ~val; + /* XXX: the chip spec is not coherent, so we add a hidden + register to distinguish between IOC and SPD */ + if (val & UHCI_STS_USBINT) + s->status2 = 0; + uhci_update_irq(s); + break; + case 0x04: + s->intr = val; + uhci_update_irq(s); + break; + case 0x06: + if (s->status & UHCI_STS_HCHALTED) + s->frnum = val & 0x7ff; + break; + case 0x10 ... 0x1f: + { + UHCIPort *port; + USBDevice *dev; + int n; + + n = (addr >> 1) & 7; + if (n >= NB_PORTS) + return; + port = &s->ports[n]; + dev = port->port.dev; + if (dev) { + /* port reset */ + if ( (val & UHCI_PORT_RESET) && + !(port->ctrl & UHCI_PORT_RESET) ) { + dev->handle_packet(dev, + USB_MSG_RESET, 0, 0, NULL, 0); + } + } + port->ctrl = (port->ctrl & 0x01fb) | (val & ~0x01fb); + /* some bits are reset when a '1' is written to them */ + port->ctrl &= ~(val & 0x000a); + } + break; + } +} + +static uint32_t uhci_ioport_readw(void *opaque, uint32_t addr) +{ + UHCIState *s = opaque; + uint32_t val; + + addr &= 0x1f; + switch(addr) { + case 0x00: + val = s->cmd; + break; + case 0x02: + val = s->status; + break; + case 0x04: + val = s->intr; + break; + case 0x06: + val = s->frnum; + break; + case 0x10 ... 0x1f: + { + UHCIPort *port; + int n; + n = (addr >> 1) & 7; + if (n >= NB_PORTS) + goto read_default; + port = &s->ports[n]; + val = port->ctrl; + } + break; + default: + read_default: + val = 0xff7f; /* disabled port */ + break; + } +#ifdef DEBUG + printf("uhci readw port=0x%04x val=0x%04x\n", addr, val); +#endif + return val; +} + +static void uhci_ioport_writel(void *opaque, uint32_t addr, uint32_t val) +{ + UHCIState *s = opaque; + + addr &= 0x1f; +#ifdef DEBUG + printf("uhci writel port=0x%04x val=0x%08x\n", addr, val); +#endif + switch(addr) { + case 0x08: + s->fl_base_addr = val & ~0xfff; + break; + } +} + +static uint32_t uhci_ioport_readl(void *opaque, uint32_t addr) +{ + UHCIState *s = opaque; + uint32_t val; + + addr &= 0x1f; + switch(addr) { + case 0x08: + val = s->fl_base_addr; + break; + default: + val = 0xffffffff; + break; + } + return val; +} + +static void uhci_attach(USBPort *port1, USBDevice *dev) +{ + UHCIState *s = port1->opaque; + UHCIPort *port = &s->ports[port1->index]; + + if (dev) { + if (port->port.dev) { + usb_attach(port1, NULL); + } + /* set connect status */ + if (!(port->ctrl & UHCI_PORT_CCS)) { + port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; + } + /* update speed */ + if (dev->speed == USB_SPEED_LOW) + port->ctrl |= UHCI_PORT_LSDA; + else + port->ctrl &= ~UHCI_PORT_LSDA; + port->port.dev = dev; + /* send the attach message */ + dev->handle_packet(dev, + USB_MSG_ATTACH, 0, 0, NULL, 0); + } else { + /* set connect status */ + if (!(port->ctrl & UHCI_PORT_CCS)) { + port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; + } + /* disable port */ + if (port->ctrl & UHCI_PORT_EN) { + port->ctrl &= ~UHCI_PORT_EN; + port->ctrl |= UHCI_PORT_ENC; + } + dev = port->port.dev; + if (dev) { + /* send the detach message */ + dev->handle_packet(dev, + USB_MSG_DETACH, 0, 0, NULL, 0); + } + port->port.dev = NULL; + } +} + +static int uhci_broadcast_packet(UHCIState *s, uint8_t pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len) +{ + UHCIPort *port; + USBDevice *dev; + int i, ret; + +#ifdef DEBUG_PACKET + { + const char *pidstr; + switch(pid) { + case USB_TOKEN_SETUP: pidstr = "SETUP"; break; + case USB_TOKEN_IN: pidstr = "IN"; break; + case USB_TOKEN_OUT: pidstr = "OUT"; break; + default: pidstr = "?"; break; + } + printf("frame %d: pid=%s addr=0x%02x ep=%d len=%d\n", + s->frnum, pidstr, devaddr, devep, len); + if (pid != USB_TOKEN_IN) { + printf(" data_out="); + for(i = 0; i < len; i++) { + printf(" %02x", data[i]); + } + printf("\n"); + } + } +#endif + for(i = 0; i < NB_PORTS; i++) { + port = &s->ports[i]; + dev = port->port.dev; + if (dev && (port->ctrl & UHCI_PORT_EN)) { + ret = dev->handle_packet(dev, pid, + devaddr, devep, + data, len); + if (ret != USB_RET_NODEV) { +#ifdef DEBUG_PACKET + { + printf(" ret=%d ", ret); + if (pid == USB_TOKEN_IN && ret > 0) { + printf("data_in="); + for(i = 0; i < ret; i++) { + printf(" %02x", data[i]); + } + } + printf("\n"); + } +#endif + return ret; + } + } + } + return USB_RET_NODEV; +} + +/* return -1 if fatal error (frame must be stopped) + 0 if TD successful + 1 if TD unsuccessful or inactive +*/ +static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) +{ + uint8_t pid; + uint8_t buf[1280]; + int len, max_len, err, ret; + + if (td->ctrl & TD_CTRL_IOC) { + *int_mask |= 0x01; + } + + if (!(td->ctrl & TD_CTRL_ACTIVE)) + return 1; + + /* TD is active */ + max_len = ((td->token >> 21) + 1) & 0x7ff; + pid = td->token & 0xff; + switch(pid) { + case USB_TOKEN_OUT: + case USB_TOKEN_SETUP: + cpu_physical_memory_read(td->buffer, buf, max_len); + ret = uhci_broadcast_packet(s, pid, + (td->token >> 8) & 0x7f, + (td->token >> 15) & 0xf, + buf, max_len); + len = max_len; + break; + case USB_TOKEN_IN: + ret = uhci_broadcast_packet(s, pid, + (td->token >> 8) & 0x7f, + (td->token >> 15) & 0xf, + buf, max_len); + if (ret >= 0) { + len = ret; + if (len > max_len) { + len = max_len; + ret = USB_RET_BABBLE; + } + if (len > 0) { + /* write the data back */ + cpu_physical_memory_write(td->buffer, buf, len); + } + } else { + len = 0; + } + break; + default: + /* invalid pid : frame interrupted */ + s->status |= UHCI_STS_HCPERR; + uhci_update_irq(s); + return -1; + } + if (td->ctrl & TD_CTRL_IOS) + td->ctrl &= ~TD_CTRL_ACTIVE; + if (ret >= 0) { + td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); + td->ctrl &= ~TD_CTRL_ACTIVE; + if (pid == USB_TOKEN_IN && + (td->ctrl & TD_CTRL_SPD) && + len < max_len) { + *int_mask |= 0x02; + /* short packet: do not update QH */ + return 1; + } else { + /* success */ + return 0; + } + } else { + switch(ret) { + default: + case USB_RET_NODEV: + do_timeout: + td->ctrl |= TD_CTRL_TIMEOUT; + err = (td->ctrl >> TD_CTRL_ERROR_SHIFT) & 3; + if (err != 0) { + err--; + if (err == 0) { + td->ctrl &= ~TD_CTRL_ACTIVE; + s->status |= UHCI_STS_USBERR; + uhci_update_irq(s); + } + } + td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) | + (err << TD_CTRL_ERROR_SHIFT); + return 1; + case USB_RET_NAK: + td->ctrl |= TD_CTRL_NAK; + if (pid == USB_TOKEN_SETUP) + goto do_timeout; + return 1; + case USB_RET_STALL: + td->ctrl |= TD_CTRL_STALL; + td->ctrl &= ~TD_CTRL_ACTIVE; + return 1; + case USB_RET_BABBLE: + td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; + td->ctrl &= ~TD_CTRL_ACTIVE; + /* frame interrupted */ + return -1; + } + } +} + +static void uhci_frame_timer(void *opaque) +{ + UHCIState *s = opaque; + int64_t expire_time; + uint32_t frame_addr, link, old_td_ctrl, val; + int int_mask, cnt, ret; + UHCI_TD td; + UHCI_QH qh; + + if (!(s->cmd & UHCI_CMD_RS)) { + qemu_del_timer(s->frame_timer); + /* set hchalted bit in status - UHCI11D 2.1.2 */ + s->status |= UHCI_STS_HCHALTED; + return; + } + frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2); + cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4); + le32_to_cpus(&link); + int_mask = 0; + cnt = FRAME_MAX_LOOPS; + while ((link & 1) == 0) { + if (--cnt == 0) + break; + /* valid frame */ + if (link & 2) { + /* QH */ + cpu_physical_memory_read(link & ~0xf, (uint8_t *)&qh, sizeof(qh)); + le32_to_cpus(&qh.link); + le32_to_cpus(&qh.el_link); + depth_first: + if (qh.el_link & 1) { + /* no element : go to next entry */ + link = qh.link; + } else if (qh.el_link & 2) { + /* QH */ + link = qh.el_link; + } else { + /* TD */ + if (--cnt == 0) + break; + cpu_physical_memory_read(qh.el_link & ~0xf, + (uint8_t *)&td, sizeof(td)); + le32_to_cpus(&td.link); + le32_to_cpus(&td.ctrl); + le32_to_cpus(&td.token); + le32_to_cpus(&td.buffer); + old_td_ctrl = td.ctrl; + ret = uhci_handle_td(s, &td, &int_mask); + /* update the status bits of the TD */ + if (old_td_ctrl != td.ctrl) { + val = cpu_to_le32(td.ctrl); + cpu_physical_memory_write((qh.el_link & ~0xf) + 4, + (const uint8_t *)&val, + sizeof(val)); + } + if (ret < 0) + break; /* interrupted frame */ + if (ret == 0) { + /* update qh element link */ + qh.el_link = td.link; + val = cpu_to_le32(qh.el_link); + cpu_physical_memory_write((link & ~0xf) + 4, + (const uint8_t *)&val, + sizeof(val)); + if (qh.el_link & 4) { + /* depth first */ + goto depth_first; + } + } + /* go to next entry */ + link = qh.link; + } + } else { + /* TD */ + cpu_physical_memory_read(link & ~0xf, (uint8_t *)&td, sizeof(td)); + le32_to_cpus(&td.link); + le32_to_cpus(&td.ctrl); + le32_to_cpus(&td.token); + le32_to_cpus(&td.buffer); + old_td_ctrl = td.ctrl; + ret = uhci_handle_td(s, &td, &int_mask); + /* update the status bits of the TD */ + if (old_td_ctrl != td.ctrl) { + val = cpu_to_le32(td.ctrl); + cpu_physical_memory_write((link & ~0xf) + 4, + (const uint8_t *)&val, + sizeof(val)); + } + if (ret < 0) + break; /* interrupted frame */ + link = td.link; + } + } + s->frnum = (s->frnum + 1) & 0x7ff; + if (int_mask) { + s->status2 |= int_mask; + s->status |= UHCI_STS_USBINT; + uhci_update_irq(s); + } + /* prepare the timer for the next frame */ + expire_time = qemu_get_clock(vm_clock) + + (ticks_per_sec / FRAME_TIMER_FREQ); + qemu_mod_timer(s->frame_timer, expire_time); +} + +static void uhci_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + UHCIState *s = (UHCIState *)pci_dev; + + register_ioport_write(addr, 32, 2, uhci_ioport_writew, s); + register_ioport_read(addr, 32, 2, uhci_ioport_readw, s); + register_ioport_write(addr, 32, 4, uhci_ioport_writel, s); + register_ioport_read(addr, 32, 4, uhci_ioport_readl, s); + register_ioport_write(addr, 32, 1, uhci_ioport_writeb, s); + register_ioport_read(addr, 32, 1, uhci_ioport_readb, s); +} + +void usb_uhci_init(PCIBus *bus, USBPort **usb_ports) +{ + UHCIState *s; + uint8_t *pci_conf; + UHCIPort *port; + int i; + + s = (UHCIState *)pci_register_device(bus, + "USB-UHCI", sizeof(UHCIState), + ((PCIDevice *)piix3_state)->devfn + 2, + NULL, NULL); + pci_conf = s->dev.config; + pci_conf[0x00] = 0x86; + pci_conf[0x01] = 0x80; + pci_conf[0x02] = 0x20; + pci_conf[0x03] = 0x70; + pci_conf[0x08] = 0x01; // revision number + pci_conf[0x09] = 0x00; + pci_conf[0x0a] = 0x03; + pci_conf[0x0b] = 0x0c; + pci_conf[0x0e] = 0x00; // header_type + pci_conf[0x3d] = 4; // interrupt pin 3 + pci_conf[0x60] = 0x10; // release number + + for(i = 0; i < NB_PORTS; i++) { + port = &s->ports[i]; + port->port.opaque = s; + port->port.index = i; + port->port.attach = uhci_attach; + usb_ports[i] = &port->port; + } + s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s); + + uhci_reset(s); + + /* Use region 4 for consistency with real hardware. BSD guests seem + to rely on this. */ + pci_register_io_region(&s->dev, 4, 0x20, + PCI_ADDRESS_SPACE_IO, uhci_map); +} diff --git a/tools/ioemu/hw/usb.c b/tools/ioemu/hw/usb.c new file mode 100644 index 0000000000..34aac5fa9b --- /dev/null +++ b/tools/ioemu/hw/usb.c @@ -0,0 +1,193 @@ +/* + * QEMU USB emulation + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +void usb_attach(USBPort *port, USBDevice *dev) +{ + port->attach(port, dev); +} + +/**********************/ +/* generic USB device helpers (you are not forced to use them when + writing your USB device driver, but they help handling the + protocol) +*/ + +#define SETUP_STATE_IDLE 0 +#define SETUP_STATE_DATA 1 +#define SETUP_STATE_ACK 2 + +int usb_generic_handle_packet(USBDevice *s, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len) +{ + int l, ret = 0; + + switch(pid) { + case USB_MSG_ATTACH: + s->state = USB_STATE_ATTACHED; + break; + case USB_MSG_DETACH: + s->state = USB_STATE_NOTATTACHED; + break; + case USB_MSG_RESET: + s->remote_wakeup = 0; + s->addr = 0; + s->state = USB_STATE_DEFAULT; + s->handle_reset(s); + break; + case USB_TOKEN_SETUP: + if (s->state < USB_STATE_DEFAULT || devaddr != s->addr) + return USB_RET_NODEV; + if (len != 8) + goto fail; + memcpy(s->setup_buf, data, 8); + s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; + s->setup_index = 0; + if (s->setup_buf[0] & USB_DIR_IN) { + ret = s->handle_control(s, + (s->setup_buf[0] << 8) | s->setup_buf[1], + (s->setup_buf[3] << 8) | s->setup_buf[2], + (s->setup_buf[5] << 8) | s->setup_buf[4], + s->setup_len, + s->data_buf); + if (ret < 0) + return ret; + if (ret < s->setup_len) + s->setup_len = ret; + s->setup_state = SETUP_STATE_DATA; + } else { + if (s->setup_len == 0) + s->setup_state = SETUP_STATE_ACK; + else + s->setup_state = SETUP_STATE_DATA; + } + break; + case USB_TOKEN_IN: + if (s->state < USB_STATE_DEFAULT || devaddr != s->addr) + return USB_RET_NODEV; + switch(devep) { + case 0: + switch(s->setup_state) { + case SETUP_STATE_ACK: + if (!(s->setup_buf[0] & USB_DIR_IN)) { + s->setup_state = SETUP_STATE_IDLE; + ret = s->handle_control(s, + (s->setup_buf[0] << 8) | s->setup_buf[1], + (s->setup_buf[3] << 8) | s->setup_buf[2], + (s->setup_buf[5] << 8) | s->setup_buf[4], + s->setup_len, + s->data_buf); + if (ret > 0) + ret = 0; + } else { + /* return 0 byte */ + } + break; + case SETUP_STATE_DATA: + if (s->setup_buf[0] & USB_DIR_IN) { + l = s->setup_len - s->setup_index; + if (l > len) + l = len; + memcpy(data, s->data_buf + s->setup_index, l); + s->setup_index += l; + if (s->setup_index >= s->setup_len) + s->setup_state = SETUP_STATE_ACK; + ret = l; + } else { + s->setup_state = SETUP_STATE_IDLE; + goto fail; + } + break; + default: + goto fail; + } + break; + default: + ret = s->handle_data(s, pid, devep, data, len); + break; + } + break; + case USB_TOKEN_OUT: + if (s->state < USB_STATE_DEFAULT || devaddr != s->addr) + return USB_RET_NODEV; + switch(devep) { + case 0: + switch(s->setup_state) { + case SETUP_STATE_ACK: + if (s->setup_buf[0] & USB_DIR_IN) { + s->setup_state = SETUP_STATE_IDLE; + /* transfer OK */ + } else { + /* ignore additionnal output */ + } + break; + case SETUP_STATE_DATA: + if (!(s->setup_buf[0] & USB_DIR_IN)) { + l = s->setup_len - s->setup_index; + if (l > len) + l = len; + memcpy(s->data_buf + s->setup_index, data, l); + s->setup_index += l; + if (s->setup_index >= s->setup_len) + s->setup_state = SETUP_STATE_ACK; + ret = l; + } else { + s->setup_state = SETUP_STATE_IDLE; + goto fail; + } + break; + default: + goto fail; + } + break; + default: + ret = s->handle_data(s, pid, devep, data, len); + break; + } + break; + default: + fail: + ret = USB_RET_STALL; + break; + } + return ret; +} + +/* XXX: fix overflow */ +int set_usb_string(uint8_t *buf, const char *str) +{ + int len, i; + uint8_t *q; + + q = buf; + len = strlen(str); + *q++ = 2 * len + 2; + *q++ = 3; + for(i = 0; i < len; i++) { + *q++ = str[i]; + *q++ = 0; + } + return q - buf; +} diff --git a/tools/ioemu/hw/usb.h b/tools/ioemu/hw/usb.h new file mode 100644 index 0000000000..05502e04db --- /dev/null +++ b/tools/ioemu/hw/usb.h @@ -0,0 +1,166 @@ +/* + * QEMU USB API + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#define USB_TOKEN_SETUP 0x2d +#define USB_TOKEN_IN 0x69 /* device -> host */ +#define USB_TOKEN_OUT 0xe1 /* host -> device */ + +/* specific usb messages, also sent in the 'pid' parameter */ +#define USB_MSG_ATTACH 0x100 +#define USB_MSG_DETACH 0x101 +#define USB_MSG_RESET 0x102 + +#define USB_RET_NODEV (-1) +#define USB_RET_NAK (-2) +#define USB_RET_STALL (-3) +#define USB_RET_BABBLE (-4) + +#define USB_SPEED_LOW 0 +#define USB_SPEED_FULL 1 +#define USB_SPEED_HIGH 2 + +#define USB_STATE_NOTATTACHED 0 +#define USB_STATE_ATTACHED 1 +//#define USB_STATE_POWERED 2 +#define USB_STATE_DEFAULT 3 +//#define USB_STATE_ADDRESS 4 +//#define USB_STATE_CONFIGURED 5 +#define USB_STATE_SUSPENDED 6 + +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_STILL_IMAGE 6 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_CDC_DATA 0x0a +#define USB_CLASS_CSCID 0x0b +#define USB_CLASS_CONTENT_SEC 0x0d +#define USB_CLASS_APP_SPEC 0xfe +#define USB_CLASS_VENDOR_SPEC 0xff + +#define USB_DIR_OUT 0 +#define USB_DIR_IN 0x80 + +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +#define USB_RECIP_MASK 0x1f +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) +#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) +#define InterfaceRequest \ + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) +#define InterfaceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) +#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) +#define EndpointOutRequest \ + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) + +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USB_DEVICE_SELF_POWERED 0 +#define USB_DEVICE_REMOTE_WAKEUP 1 + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +typedef struct USBPort USBPort; +typedef struct USBDevice USBDevice; + +/* definition of a USB device */ +struct USBDevice { + void *opaque; + int (*handle_packet)(USBDevice *dev, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len); + int speed; + + /* The following fields are used by the generic USB device + layer. They are here just to avoid creating a new structure for + them. */ + void (*handle_reset)(USBDevice *dev); + int (*handle_control)(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data); + int (*handle_data)(USBDevice *dev, int pid, uint8_t devep, + uint8_t *data, int len); + uint8_t addr; + + int state; + uint8_t setup_buf[8]; + uint8_t data_buf[1024]; + int remote_wakeup; + int setup_state; + int setup_len; + int setup_index; +}; + +/* USB port on which a device can be connected */ +struct USBPort { + USBDevice *dev; + void (*attach)(USBPort *port, USBDevice *dev); + void *opaque; + int index; /* internal port index, may be used with the opaque */ +}; + +void usb_attach(USBPort *port, USBDevice *dev); +int usb_generic_handle_packet(USBDevice *s, int pid, + uint8_t devaddr, uint8_t devep, + uint8_t *data, int len); +int set_usb_string(uint8_t *buf, const char *str); + +/* usb hub */ +USBDevice *usb_hub_init(USBPort **usb_ports, int nb_ports); + +/* usb-uhci.c */ +void usb_uhci_init(PCIBus *bus, USBPort **usb_ports); + +/* usb-linux.c */ +USBDevice *usb_host_device_open(const char *devname); +void usb_host_info(void); + +/* usb-hid.c */ +USBDevice *usb_mouse_init(void); +USBDevice *usb_tablet_init(void); diff --git a/tools/ioemu/hw/versatilepb.c b/tools/ioemu/hw/versatilepb.c new file mode 100644 index 0000000000..e198a518ea --- /dev/null +++ b/tools/ioemu/hw/versatilepb.c @@ -0,0 +1,271 @@ +/* + * ARM Versatile Platform/Application Baseboard System emulation. + * + * Copyright (c) 2005-2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GPL. + */ + +#include "vl.h" +#include "arm_pic.h" + +/* Primary interrupt controller. */ + +typedef struct vpb_sic_state +{ + arm_pic_handler handler; + uint32_t base; + uint32_t level; + uint32_t mask; + uint32_t pic_enable; + void *parent; + int irq; +} vpb_sic_state; + +static void vpb_sic_update(vpb_sic_state *s) +{ + uint32_t flags; + + flags = s->level & s->mask; + pic_set_irq_new(s->parent, s->irq, flags != 0); +} + +static void vpb_sic_update_pic(vpb_sic_state *s) +{ + int i; + uint32_t mask; + + for (i = 21; i <= 30; i++) { + mask = 1u << i; + if (!(s->pic_enable & mask)) + continue; + pic_set_irq_new(s->parent, i, (s->level & mask) != 0); + } +} + +static void vpb_sic_set_irq(void *opaque, int irq, int level) +{ + vpb_sic_state *s = (vpb_sic_state *)opaque; + if (level) + s->level |= 1u << irq; + else + s->level &= ~(1u << irq); + if (s->pic_enable & (1u << irq)) + pic_set_irq_new(s->parent, irq, level); + vpb_sic_update(s); +} + +static uint32_t vpb_sic_read(void *opaque, target_phys_addr_t offset) +{ + vpb_sic_state *s = (vpb_sic_state *)opaque; + + offset -= s->base; + switch (offset >> 2) { + case 0: /* STATUS */ + return s->level & s->mask; + case 1: /* RAWSTAT */ + return s->level; + case 2: /* ENABLE */ + return s->mask; + case 4: /* SOFTINT */ + return s->level & 1; + case 8: /* PICENABLE */ + return s->pic_enable; + default: + printf ("vpb_sic_read: Bad register offset 0x%x\n", offset); + return 0; + } +} + +static void vpb_sic_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + vpb_sic_state *s = (vpb_sic_state *)opaque; + offset -= s->base; + + switch (offset >> 2) { + case 2: /* ENSET */ + s->mask |= value; + break; + case 3: /* ENCLR */ + s->mask &= ~value; + break; + case 4: /* SOFTINTSET */ + if (value) + s->mask |= 1; + break; + case 5: /* SOFTINTCLR */ + if (value) + s->mask &= ~1u; + break; + case 8: /* PICENSET */ + s->pic_enable |= (value & 0x7fe00000); + vpb_sic_update_pic(s); + break; + case 9: /* PICENCLR */ + s->pic_enable &= ~value; + vpb_sic_update_pic(s); + break; + default: + printf ("vpb_sic_write: Bad register offset 0x%x\n", offset); + return; + } + vpb_sic_update(s); +} + +static CPUReadMemoryFunc *vpb_sic_readfn[] = { + vpb_sic_read, + vpb_sic_read, + vpb_sic_read +}; + +static CPUWriteMemoryFunc *vpb_sic_writefn[] = { + vpb_sic_write, + vpb_sic_write, + vpb_sic_write +}; + +static vpb_sic_state *vpb_sic_init(uint32_t base, void *parent, int irq) +{ + vpb_sic_state *s; + int iomemtype; + + s = (vpb_sic_state *)qemu_mallocz(sizeof(vpb_sic_state)); + if (!s) + return NULL; + s->handler = vpb_sic_set_irq; + s->base = base; + s->parent = parent; + s->irq = irq; + iomemtype = cpu_register_io_memory(0, vpb_sic_readfn, + vpb_sic_writefn, s); + cpu_register_physical_memory(base, 0x00000fff, iomemtype); + /* ??? Save/restore. */ + return s; +} + +/* Board init. */ + +/* The AB and PB boards both use the same core, just with different + peripherans and expansion busses. For now we emulate a subset of the + PB peripherals and just change the board ID. */ + +static void versatile_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, int board_id) +{ + CPUState *env; + void *pic; + void *sic; + + env = cpu_init(); + cpu_arm_set_model(env, ARM_CPUID_ARM926); + /* ??? RAM shoud repeat to fill physical memory space. */ + /* SDRAM at address zero. */ + cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); + + pic = arm_pic_init_cpu(env); + pic = pl190_init(0x10140000, pic, ARM_PIC_CPU_IRQ, ARM_PIC_CPU_FIQ); + sic = vpb_sic_init(0x10003000, pic, 31); + pl050_init(0x10006000, sic, 3, 0); + pl050_init(0x10007000, sic, 4, 1); + + /* TODO: Init PCI NICs. */ + if (nd_table[0].vlan) { + if (nd_table[0].model == NULL + || strcmp(nd_table[0].model, "smc91c111") == 0) { + smc91c111_init(&nd_table[0], 0x10010000, sic, 25); + } else { + fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model); + exit (1); + } + } + + pl011_init(0x101f1000, pic, 12, serial_hds[0]); + pl011_init(0x101f2000, pic, 13, serial_hds[1]); + pl011_init(0x101f3000, pic, 14, serial_hds[2]); + pl011_init(0x10009000, sic, 6, serial_hds[3]); + + pl080_init(0x10130000, pic, 17); + sp804_init(0x101e2000, pic, 4); + sp804_init(0x101e3000, pic, 5); + + /* The versatile/PB actually has a modified Color LCD controller + that includes hardware cursor support from the PL111. */ + pl110_init(ds, 0x10120000, pic, 16, 1); + + /* Memory map for Versatile/PB: */ + /* 0x10000000 System registers. */ + /* 0x10001000 PCI controller config registers. */ + /* 0x10002000 Serial bus interface. */ + /* 0x10003000 Secondary interrupt controller. */ + /* 0x10004000 AACI (audio). */ + /* 0x10005000 MMCI0. */ + /* 0x10006000 KMI0 (keyboard). */ + /* 0x10007000 KMI1 (mouse). */ + /* 0x10008000 Character LCD Interface. */ + /* 0x10009000 UART3. */ + /* 0x1000a000 Smart card 1. */ + /* 0x1000b000 MMCI1. */ + /* 0x10010000 Ethernet. */ + /* 0x10020000 USB. */ + /* 0x10100000 SSMC. */ + /* 0x10110000 MPMC. */ + /* 0x10120000 CLCD Controller. */ + /* 0x10130000 DMA Controller. */ + /* 0x10140000 Vectored interrupt controller. */ + /* 0x101d0000 AHB Monitor Interface. */ + /* 0x101e0000 System Controller. */ + /* 0x101e1000 Watchdog Interface. */ + /* 0x101e2000 Timer 0/1. */ + /* 0x101e3000 Timer 2/3. */ + /* 0x101e4000 GPIO port 0. */ + /* 0x101e5000 GPIO port 1. */ + /* 0x101e6000 GPIO port 2. */ + /* 0x101e7000 GPIO port 3. */ + /* 0x101e8000 RTC. */ + /* 0x101f0000 Smart card 0. */ + /* 0x101f1000 UART0. */ + /* 0x101f2000 UART1. */ + /* 0x101f3000 UART2. */ + /* 0x101f4000 SSPI. */ + + arm_load_kernel(ram_size, kernel_filename, kernel_cmdline, + initrd_filename, board_id); +} + +static void vpb_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + versatile_init(ram_size, vga_ram_size, boot_device, + ds, fd_filename, snapshot, + kernel_filename, kernel_cmdline, + initrd_filename, 0x183); +} + +static void vab_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + versatile_init(ram_size, vga_ram_size, boot_device, + ds, fd_filename, snapshot, + kernel_filename, kernel_cmdline, + initrd_filename, 0x25e); +} + +QEMUMachine versatilepb_machine = { + "versatilepb", + "ARM Versatile/PB (ARM926EJ-S)", + vpb_init, +}; + +QEMUMachine versatileab_machine = { + "versatileab", + "ARM Versatile/AB (ARM926EJ-S)", + vab_init, +}; diff --git a/tools/ioemu/hw/vga.c b/tools/ioemu/hw/vga.c new file mode 100644 index 0000000000..7ff5de738c --- /dev/null +++ b/tools/ioemu/hw/vga.c @@ -0,0 +1,2029 @@ +/* + * QEMU VGA Emulator. + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "vga_int.h" + +//#define DEBUG_VGA +//#define DEBUG_VGA_MEM +//#define DEBUG_VGA_REG + +//#define DEBUG_BOCHS_VBE + +/* force some bits to zero */ +const uint8_t sr_mask[8] = { + (uint8_t)~0xfc, + (uint8_t)~0xc2, + (uint8_t)~0xf0, + (uint8_t)~0xc0, + (uint8_t)~0xf1, + (uint8_t)~0xff, + (uint8_t)~0xff, + (uint8_t)~0x00, +}; + +const uint8_t gr_mask[16] = { + (uint8_t)~0xf0, /* 0x00 */ + (uint8_t)~0xf0, /* 0x01 */ + (uint8_t)~0xf0, /* 0x02 */ + (uint8_t)~0xe0, /* 0x03 */ + (uint8_t)~0xfc, /* 0x04 */ + (uint8_t)~0x84, /* 0x05 */ + (uint8_t)~0xf0, /* 0x06 */ + (uint8_t)~0xf0, /* 0x07 */ + (uint8_t)~0x00, /* 0x08 */ + (uint8_t)~0xff, /* 0x09 */ + (uint8_t)~0xff, /* 0x0a */ + (uint8_t)~0xff, /* 0x0b */ + (uint8_t)~0xff, /* 0x0c */ + (uint8_t)~0xff, /* 0x0d */ + (uint8_t)~0xff, /* 0x0e */ + (uint8_t)~0xff, /* 0x0f */ +}; + +#define cbswap_32(__x) \ +((uint32_t)( \ + (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) + +#ifdef WORDS_BIGENDIAN +#define PAT(x) cbswap_32(x) +#else +#define PAT(x) (x) +#endif + +#ifdef WORDS_BIGENDIAN +#define BIG 1 +#else +#define BIG 0 +#endif + +#ifdef WORDS_BIGENDIAN +#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff) +#else +#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff) +#endif + +static const uint32_t mask16[16] = { + PAT(0x00000000), + PAT(0x000000ff), + PAT(0x0000ff00), + PAT(0x0000ffff), + PAT(0x00ff0000), + PAT(0x00ff00ff), + PAT(0x00ffff00), + PAT(0x00ffffff), + PAT(0xff000000), + PAT(0xff0000ff), + PAT(0xff00ff00), + PAT(0xff00ffff), + PAT(0xffff0000), + PAT(0xffff00ff), + PAT(0xffffff00), + PAT(0xffffffff), +}; + +#undef PAT + +#ifdef WORDS_BIGENDIAN +#define PAT(x) (x) +#else +#define PAT(x) cbswap_32(x) +#endif + +static const uint32_t dmask16[16] = { + PAT(0x00000000), + PAT(0x000000ff), + PAT(0x0000ff00), + PAT(0x0000ffff), + PAT(0x00ff0000), + PAT(0x00ff00ff), + PAT(0x00ffff00), + PAT(0x00ffffff), + PAT(0xff000000), + PAT(0xff0000ff), + PAT(0xff00ff00), + PAT(0xff00ffff), + PAT(0xffff0000), + PAT(0xffff00ff), + PAT(0xffffff00), + PAT(0xffffffff), +}; + +static const uint32_t dmask4[4] = { + PAT(0x00000000), + PAT(0x0000ffff), + PAT(0xffff0000), + PAT(0xffffffff), +}; + +static uint32_t expand4[256]; +static uint16_t expand2[256]; +static uint8_t expand4to8[16]; + +VGAState *vga_state; +int vga_io_memory; + +static void vga_screen_dump(void *opaque, const char *filename); + +static uint32_t vga_ioport_read(void *opaque, uint32_t addr) +{ + VGAState *s = opaque; + int val, index; + + /* check port range access depending on color/monochrome mode */ + if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) || + (addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION))) { + val = 0xff; + } else { + switch(addr) { + case 0x3c0: + if (s->ar_flip_flop == 0) { + val = s->ar_index; + } else { + val = 0; + } + break; + case 0x3c1: + index = s->ar_index & 0x1f; + if (index < 21) + val = s->ar[index]; + else + val = 0; + break; + case 0x3c2: + val = s->st00; + break; + case 0x3c4: + val = s->sr_index; + break; + case 0x3c5: + val = s->sr[s->sr_index]; +#ifdef DEBUG_VGA_REG + printf("vga: read SR%x = 0x%02x\n", s->sr_index, val); +#endif + break; + case 0x3c7: + val = s->dac_state; + break; + case 0x3c8: + val = s->dac_write_index; + break; + case 0x3c9: + val = s->palette[s->dac_read_index * 3 + s->dac_sub_index]; + if (++s->dac_sub_index == 3) { + s->dac_sub_index = 0; + s->dac_read_index++; + } + break; + case 0x3ca: + val = s->fcr; + break; + case 0x3cc: + val = s->msr; + break; + case 0x3ce: + val = s->gr_index; + break; + case 0x3cf: + val = s->gr[s->gr_index]; +#ifdef DEBUG_VGA_REG + printf("vga: read GR%x = 0x%02x\n", s->gr_index, val); +#endif + break; + case 0x3b4: + case 0x3d4: + val = s->cr_index; + break; + case 0x3b5: + case 0x3d5: + val = s->cr[s->cr_index]; +#ifdef DEBUG_VGA_REG + printf("vga: read CR%x = 0x%02x\n", s->cr_index, val); +#endif + break; + case 0x3ba: + case 0x3da: + /* just toggle to fool polling */ + s->st01 ^= ST01_V_RETRACE | ST01_DISP_ENABLE; + val = s->st01; + s->ar_flip_flop = 0; + break; + default: + val = 0x00; + break; + } + } +#if defined(DEBUG_VGA) + printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val); +#endif + return val; +} + +static void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + VGAState *s = opaque; + int index; + + /* check port range access depending on color/monochrome mode */ + if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) || + (addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION))) + return; + +#ifdef DEBUG_VGA + printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val); +#endif + + switch(addr) { + case 0x3c0: + if (s->ar_flip_flop == 0) { + val &= 0x3f; + s->ar_index = val; + } else { + index = s->ar_index & 0x1f; + switch(index) { + case 0x00 ... 0x0f: + s->ar[index] = val & 0x3f; + break; + case 0x10: + s->ar[index] = val & ~0x10; + break; + case 0x11: + s->ar[index] = val; + break; + case 0x12: + s->ar[index] = val & ~0xc0; + break; + case 0x13: + s->ar[index] = val & ~0xf0; + break; + case 0x14: + s->ar[index] = val & ~0xf0; + break; + default: + break; + } + } + s->ar_flip_flop ^= 1; + break; + case 0x3c2: + s->msr = val & ~0x10; + break; + case 0x3c4: + s->sr_index = val & 7; + break; + case 0x3c5: +#ifdef DEBUG_VGA_REG + printf("vga: write SR%x = 0x%02x\n", s->sr_index, val); +#endif + s->sr[s->sr_index] = val & sr_mask[s->sr_index]; + break; + case 0x3c7: + s->dac_read_index = val; + s->dac_sub_index = 0; + s->dac_state = 3; + break; + case 0x3c8: + s->dac_write_index = val; + s->dac_sub_index = 0; + s->dac_state = 0; + break; + case 0x3c9: + s->dac_cache[s->dac_sub_index] = val; + if (++s->dac_sub_index == 3) { + memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3); + s->dac_sub_index = 0; + s->dac_write_index++; + } + break; + case 0x3ce: + s->gr_index = val & 0x0f; + break; + case 0x3cf: +#ifdef DEBUG_VGA_REG + printf("vga: write GR%x = 0x%02x\n", s->gr_index, val); +#endif + s->gr[s->gr_index] = val & gr_mask[s->gr_index]; + break; + case 0x3b4: + case 0x3d4: + s->cr_index = val; + break; + case 0x3b5: + case 0x3d5: +#ifdef DEBUG_VGA_REG + printf("vga: write CR%x = 0x%02x\n", s->cr_index, val); +#endif + /* handle CR0-7 protection */ + if ((s->cr[0x11] & 0x80) && s->cr_index <= 7) { + /* can always write bit 4 of CR7 */ + if (s->cr_index == 7) + s->cr[7] = (s->cr[7] & ~0x10) | (val & 0x10); + return; + } + switch(s->cr_index) { + case 0x01: /* horizontal display end */ + case 0x07: + case 0x09: + case 0x0c: + case 0x0d: + case 0x12: /* veritcal display end */ + s->cr[s->cr_index] = val; + break; + default: + s->cr[s->cr_index] = val; + break; + } + break; + case 0x3ba: + case 0x3da: + s->fcr = val & 0x10; + break; + } +} + +#ifdef CONFIG_BOCHS_VBE +static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr) +{ + VGAState *s = opaque; + uint32_t val; + val = s->vbe_index; + return val; +} + +static uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr) +{ + VGAState *s = opaque; + uint32_t val; + + if (s->vbe_index <= VBE_DISPI_INDEX_NB) + val = s->vbe_regs[s->vbe_index]; + else + val = 0; +#ifdef DEBUG_BOCHS_VBE + printf("VBE: read index=0x%x val=0x%x\n", s->vbe_index, val); +#endif + return val; +} + +static void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val) +{ + VGAState *s = opaque; + s->vbe_index = val; +} + +static void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val) +{ + VGAState *s = opaque; + + if (s->vbe_index <= VBE_DISPI_INDEX_NB) { +#ifdef DEBUG_BOCHS_VBE + printf("VBE: write index=0x%x val=0x%x\n", s->vbe_index, val); +#endif + switch(s->vbe_index) { + case VBE_DISPI_INDEX_ID: + if (val == VBE_DISPI_ID0 || + val == VBE_DISPI_ID1 || + val == VBE_DISPI_ID2) { + s->vbe_regs[s->vbe_index] = val; + } + break; + case VBE_DISPI_INDEX_XRES: + if ((val <= VBE_DISPI_MAX_XRES) && ((val & 7) == 0)) { + s->vbe_regs[s->vbe_index] = val; + } + break; + case VBE_DISPI_INDEX_YRES: + if (val <= VBE_DISPI_MAX_YRES) { + s->vbe_regs[s->vbe_index] = val; + } + break; + case VBE_DISPI_INDEX_BPP: + if (val == 0) + val = 8; + if (val == 4 || val == 8 || val == 15 || + val == 16 || val == 24 || val == 32) { + s->vbe_regs[s->vbe_index] = val; + } + break; + case VBE_DISPI_INDEX_BANK: + val &= s->vbe_bank_mask; + s->vbe_regs[s->vbe_index] = val; + s->bank_offset = (val << 16); + break; + case VBE_DISPI_INDEX_ENABLE: + if (val & VBE_DISPI_ENABLED) { + int h, shift_control; + + s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = + s->vbe_regs[VBE_DISPI_INDEX_XRES]; + s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = + s->vbe_regs[VBE_DISPI_INDEX_YRES]; + s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0; + s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0; + + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) + s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1; + else + s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] * + ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); + s->vbe_start_addr = 0; + + /* clear the screen (should be done in BIOS) */ + if (!(val & VBE_DISPI_NOCLEARMEM)) { + memset(s->vram_ptr, 0, + s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset); + } + + /* we initialize the VGA graphic mode (should be done + in BIOS) */ + s->gr[0x06] = (s->gr[0x06] & ~0x0c) | 0x05; /* graphic mode + memory map 1 */ + s->cr[0x17] |= 3; /* no CGA modes */ + s->cr[0x13] = s->vbe_line_offset >> 3; + /* width */ + s->cr[0x01] = (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1; + /* height */ + h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1; + s->cr[0x12] = h; + s->cr[0x07] = (s->cr[0x07] & ~0x42) | + ((h >> 7) & 0x02) | ((h >> 3) & 0x40); + /* line compare to 1023 */ + s->cr[0x18] = 0xff; + s->cr[0x07] |= 0x10; + s->cr[0x09] |= 0x40; + + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) { + shift_control = 0; + s->sr[0x01] &= ~8; /* no double line */ + } else { + shift_control = 2; + s->sr[4] |= 0x08; /* set chain 4 mode */ + s->sr[2] |= 0x0f; /* activate all planes */ + } + s->gr[0x05] = (s->gr[0x05] & ~0x60) | (shift_control << 5); + s->cr[0x09] &= ~0x9f; /* no double scan */ + } else { + /* XXX: the bios should do that */ + s->bank_offset = 0; + } + s->vbe_regs[s->vbe_index] = val; + break; + case VBE_DISPI_INDEX_VIRT_WIDTH: + { + int w, h, line_offset; + + if (val < s->vbe_regs[VBE_DISPI_INDEX_XRES]) + return; + w = val; + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) + line_offset = w >> 1; + else + line_offset = w * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); + h = s->vram_size / line_offset; + /* XXX: support weird bochs semantics ? */ + if (h < s->vbe_regs[VBE_DISPI_INDEX_YRES]) + return; + s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = w; + s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = h; + s->vbe_line_offset = line_offset; + } + break; + case VBE_DISPI_INDEX_X_OFFSET: + case VBE_DISPI_INDEX_Y_OFFSET: + { + int x; + s->vbe_regs[s->vbe_index] = val; + s->vbe_start_addr = s->vbe_line_offset * s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]; + x = s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET]; + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) + s->vbe_start_addr += x >> 1; + else + s->vbe_start_addr += x * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); + s->vbe_start_addr >>= 2; + } + break; + default: + break; + } + } +} +#endif + +/* called for accesses between 0xa0000 and 0xc0000 */ +uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr) +{ + VGAState *s = opaque; + int memory_map_mode, plane; + uint32_t ret; + + /* convert to VGA memory offset */ + memory_map_mode = (s->gr[6] >> 2) & 3; + addr &= 0x1ffff; + switch(memory_map_mode) { + case 0: + break; + case 1: + if (addr >= 0x10000) + return 0xff; + addr += s->bank_offset; + break; + case 2: + addr -= 0x10000; + if (addr >= 0x8000) + return 0xff; + break; + default: + case 3: + addr -= 0x18000; + if (addr >= 0x8000) + return 0xff; + break; + } + + if (s->sr[4] & 0x08) { + /* chain 4 mode : simplest access */ + ret = s->vram_ptr[addr]; + } else if (s->gr[5] & 0x10) { + /* odd/even mode (aka text mode mapping) */ + plane = (s->gr[4] & 2) | (addr & 1); + ret = s->vram_ptr[((addr & ~1) << 1) | plane]; + } else { + /* standard VGA latched access */ + s->latch = ((uint32_t *)s->vram_ptr)[addr]; + + if (!(s->gr[5] & 0x08)) { + /* read mode 0 */ + plane = s->gr[4]; + ret = GET_PLANE(s->latch, plane); + } else { + /* read mode 1 */ + ret = (s->latch ^ mask16[s->gr[2]]) & mask16[s->gr[7]]; + ret |= ret >> 16; + ret |= ret >> 8; + ret = (~ret) & 0xff; + } + } + return ret; +} + +static uint32_t vga_mem_readw(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; +#ifdef TARGET_WORDS_BIGENDIAN + v = vga_mem_readb(opaque, addr) << 8; + v |= vga_mem_readb(opaque, addr + 1); +#else + v = vga_mem_readb(opaque, addr); + v |= vga_mem_readb(opaque, addr + 1) << 8; +#endif + return v; +} + +static uint32_t vga_mem_readl(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; +#ifdef TARGET_WORDS_BIGENDIAN + v = vga_mem_readb(opaque, addr) << 24; + v |= vga_mem_readb(opaque, addr + 1) << 16; + v |= vga_mem_readb(opaque, addr + 2) << 8; + v |= vga_mem_readb(opaque, addr + 3); +#else + v = vga_mem_readb(opaque, addr); + v |= vga_mem_readb(opaque, addr + 1) << 8; + v |= vga_mem_readb(opaque, addr + 2) << 16; + v |= vga_mem_readb(opaque, addr + 3) << 24; +#endif + return v; +} + +/* called for accesses between 0xa0000 and 0xc0000 */ +void vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + VGAState *s = opaque; + int memory_map_mode, plane, write_mode, b, func_select, mask; + uint32_t write_mask, bit_mask, set_mask; + +#ifdef DEBUG_VGA_MEM + printf("vga: [0x%x] = 0x%02x\n", addr, val); +#endif + /* convert to VGA memory offset */ + memory_map_mode = (s->gr[6] >> 2) & 3; + addr &= 0x1ffff; + switch(memory_map_mode) { + case 0: + break; + case 1: + if (addr >= 0x10000) + return; + addr += s->bank_offset; + break; + case 2: + addr -= 0x10000; + if (addr >= 0x8000) + return; + break; + default: + case 3: + addr -= 0x18000; + if (addr >= 0x8000) + return; + break; + } + + if (s->sr[4] & 0x08) { + /* chain 4 mode : simplest access */ + plane = addr & 3; + mask = (1 << plane); + if (s->sr[2] & mask) { + s->vram_ptr[addr] = val; +#ifdef DEBUG_VGA_MEM + printf("vga: chain4: [0x%x]\n", addr); +#endif + s->plane_updated |= mask; /* only used to detect font change */ + cpu_physical_memory_set_dirty(s->vram_offset + addr); + } + } else if (s->gr[5] & 0x10) { + /* odd/even mode (aka text mode mapping) */ + plane = (s->gr[4] & 2) | (addr & 1); + mask = (1 << plane); + if (s->sr[2] & mask) { + addr = ((addr & ~1) << 1) | plane; + s->vram_ptr[addr] = val; +#ifdef DEBUG_VGA_MEM + printf("vga: odd/even: [0x%x]\n", addr); +#endif + s->plane_updated |= mask; /* only used to detect font change */ + cpu_physical_memory_set_dirty(s->vram_offset + addr); + } + } else { + /* standard VGA latched access */ + write_mode = s->gr[5] & 3; + switch(write_mode) { + default: + case 0: + /* rotate */ + b = s->gr[3] & 7; + val = ((val >> b) | (val << (8 - b))) & 0xff; + val |= val << 8; + val |= val << 16; + + /* apply set/reset mask */ + set_mask = mask16[s->gr[1]]; + val = (val & ~set_mask) | (mask16[s->gr[0]] & set_mask); + bit_mask = s->gr[8]; + break; + case 1: + val = s->latch; + goto do_write; + case 2: + val = mask16[val & 0x0f]; + bit_mask = s->gr[8]; + break; + case 3: + /* rotate */ + b = s->gr[3] & 7; + val = (val >> b) | (val << (8 - b)); + + bit_mask = s->gr[8] & val; + val = mask16[s->gr[0]]; + break; + } + + /* apply logical operation */ + func_select = s->gr[3] >> 3; + switch(func_select) { + case 0: + default: + /* nothing to do */ + break; + case 1: + /* and */ + val &= s->latch; + break; + case 2: + /* or */ + val |= s->latch; + break; + case 3: + /* xor */ + val ^= s->latch; + break; + } + + /* apply bit mask */ + bit_mask |= bit_mask << 8; + bit_mask |= bit_mask << 16; + val = (val & bit_mask) | (s->latch & ~bit_mask); + + do_write: + /* mask data according to sr[2] */ + mask = s->sr[2]; + s->plane_updated |= mask; /* only used to detect font change */ + write_mask = mask16[mask]; + ((uint32_t *)s->vram_ptr)[addr] = + (((uint32_t *)s->vram_ptr)[addr] & ~write_mask) | + (val & write_mask); +#ifdef DEBUG_VGA_MEM + printf("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n", + addr * 4, write_mask, val); +#endif + cpu_physical_memory_set_dirty(s->vram_offset + (addr << 2)); + } +} + +static void vga_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ +#ifdef TARGET_WORDS_BIGENDIAN + vga_mem_writeb(opaque, addr, (val >> 8) & 0xff); + vga_mem_writeb(opaque, addr + 1, val & 0xff); +#else + vga_mem_writeb(opaque, addr, val & 0xff); + vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff); +#endif +} + +static void vga_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ +#ifdef TARGET_WORDS_BIGENDIAN + vga_mem_writeb(opaque, addr, (val >> 24) & 0xff); + vga_mem_writeb(opaque, addr + 1, (val >> 16) & 0xff); + vga_mem_writeb(opaque, addr + 2, (val >> 8) & 0xff); + vga_mem_writeb(opaque, addr + 3, val & 0xff); +#else + vga_mem_writeb(opaque, addr, val & 0xff); + vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff); + vga_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff); + vga_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff); +#endif +} + +typedef void vga_draw_glyph8_func(uint8_t *d, int linesize, + const uint8_t *font_ptr, int h, + uint32_t fgcol, uint32_t bgcol); +typedef void vga_draw_glyph9_func(uint8_t *d, int linesize, + const uint8_t *font_ptr, int h, + uint32_t fgcol, uint32_t bgcol, int dup9); +typedef void vga_draw_line_func(VGAState *s1, uint8_t *d, + const uint8_t *s, int width); + +static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b) +{ + return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6); +} + +static inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b) +{ + return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); +} + +static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b) +{ + return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); +} + +static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b) +{ + return (r << 16) | (g << 8) | b; +} + +#define DEPTH 8 +#include "vga_template.h" + +#define DEPTH 15 +#include "vga_template.h" + +#define DEPTH 16 +#include "vga_template.h" + +#define DEPTH 32 +#include "vga_template.h" + +static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel8(r, g, b); + col |= col << 8; + col |= col << 16; + return col; +} + +static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel15(r, g, b); + col |= col << 16; + return col; +} + +static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel16(r, g, b); + col |= col << 16; + return col; +} + +static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel32(r, g, b); + return col; +} + +/* return true if the palette was modified */ +static int update_palette16(VGAState *s) +{ + int full_update, i; + uint32_t v, col, *palette; + + full_update = 0; + palette = s->last_palette; + for(i = 0; i < 16; i++) { + v = s->ar[i]; + if (s->ar[0x10] & 0x80) + v = ((s->ar[0x14] & 0xf) << 4) | (v & 0xf); + else + v = ((s->ar[0x14] & 0xc) << 4) | (v & 0x3f); + v = v * 3; + col = s->rgb_to_pixel(c6_to_8(s->palette[v]), + c6_to_8(s->palette[v + 1]), + c6_to_8(s->palette[v + 2])); + if (col != palette[i]) { + full_update = 1; + palette[i] = col; + } + } + return full_update; +} + +/* return true if the palette was modified */ +static int update_palette256(VGAState *s) +{ + int full_update, i; + uint32_t v, col, *palette; + + full_update = 0; + palette = s->last_palette; + v = 0; + for(i = 0; i < 256; i++) { + col = s->rgb_to_pixel(c6_to_8(s->palette[v]), + c6_to_8(s->palette[v + 1]), + c6_to_8(s->palette[v + 2])); + if (col != palette[i]) { + full_update = 1; + palette[i] = col; + } + v += 3; + } + return full_update; +} + +static void vga_get_offsets(VGAState *s, + uint32_t *pline_offset, + uint32_t *pstart_addr) +{ + uint32_t start_addr, line_offset; +#ifdef CONFIG_BOCHS_VBE + if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { + line_offset = s->vbe_line_offset; + start_addr = s->vbe_start_addr; + } else +#endif + { + /* compute line_offset in bytes */ + line_offset = s->cr[0x13]; + line_offset <<= 3; + + /* starting address */ + start_addr = s->cr[0x0d] | (s->cr[0x0c] << 8); + } + *pline_offset = line_offset; + *pstart_addr = start_addr; +} + +/* update start_addr and line_offset. Return TRUE if modified */ +static int update_basic_params(VGAState *s) +{ + int full_update; + uint32_t start_addr, line_offset, line_compare; + + full_update = 0; + + s->get_offsets(s, &line_offset, &start_addr); + /* line compare */ + line_compare = s->cr[0x18] | + ((s->cr[0x07] & 0x10) << 4) | + ((s->cr[0x09] & 0x40) << 3); + + if (line_offset != s->line_offset || + start_addr != s->start_addr || + line_compare != s->line_compare) { + s->line_offset = line_offset; + s->start_addr = start_addr; + s->line_compare = line_compare; + full_update = 1; + } + return full_update; +} + +static inline int get_depth_index(int depth) +{ + switch(depth) { + default: + case 8: + return 0; + case 15: + return 1; + case 16: + return 2; + case 32: + return 3; + } +} + +static vga_draw_glyph8_func *vga_draw_glyph8_table[4] = { + vga_draw_glyph8_8, + vga_draw_glyph8_16, + vga_draw_glyph8_16, + vga_draw_glyph8_32, +}; + +static vga_draw_glyph8_func *vga_draw_glyph16_table[4] = { + vga_draw_glyph16_8, + vga_draw_glyph16_16, + vga_draw_glyph16_16, + vga_draw_glyph16_32, +}; + +static vga_draw_glyph9_func *vga_draw_glyph9_table[4] = { + vga_draw_glyph9_8, + vga_draw_glyph9_16, + vga_draw_glyph9_16, + vga_draw_glyph9_32, +}; + +static const uint8_t cursor_glyph[32 * 4] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +/* + * Text mode update + * Missing: + * - double scan + * - double width + * - underline + * - flashing + */ +static void vga_draw_text(VGAState *s, int full_update) +{ + int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr; + int cx_min, cx_max, linesize, x_incr; + uint32_t offset, fgcol, bgcol, v, cursor_offset; + uint8_t *d1, *d, *src, *s1, *dest, *cursor_ptr; + const uint8_t *font_ptr, *font_base[2]; + int dup9, line_offset, depth_index; + uint32_t *palette; + uint32_t *ch_attr_ptr; + vga_draw_glyph8_func *vga_draw_glyph8; + vga_draw_glyph9_func *vga_draw_glyph9; + + full_update |= update_palette16(s); + palette = s->last_palette; + + /* compute font data address (in plane 2) */ + v = s->sr[3]; + offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2; + if (offset != s->font_offsets[0]) { + s->font_offsets[0] = offset; + full_update = 1; + } + font_base[0] = s->vram_ptr + offset; + + offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2; + font_base[1] = s->vram_ptr + offset; + if (offset != s->font_offsets[1]) { + s->font_offsets[1] = offset; + full_update = 1; + } + if (s->plane_updated & (1 << 2)) { + /* if the plane 2 was modified since the last display, it + indicates the font may have been modified */ + s->plane_updated = 0; + full_update = 1; + } + full_update |= update_basic_params(s); + + line_offset = s->line_offset; + s1 = s->vram_ptr + (s->start_addr * 4); + + /* total width & height */ + cheight = (s->cr[9] & 0x1f) + 1; + cw = 8; + if (!(s->sr[1] & 0x01)) + cw = 9; + if (s->sr[1] & 0x08) + cw = 16; /* NOTE: no 18 pixel wide */ + x_incr = cw * ((s->ds->depth + 7) >> 3); + width = (s->cr[0x01] + 1); + if (s->cr[0x06] == 100) { + /* ugly hack for CGA 160x100x16 - explain me the logic */ + height = 100; + } else { + height = s->cr[0x12] | + ((s->cr[0x07] & 0x02) << 7) | + ((s->cr[0x07] & 0x40) << 3); + height = (height + 1) / cheight; + } + if ((height * width) > CH_ATTR_SIZE) { + /* better than nothing: exit if transient size is too big */ + return; + } + + if (width != s->last_width || height != s->last_height || + cw != s->last_cw || cheight != s->last_ch) { + s->last_scr_width = width * cw; + s->last_scr_height = height * cheight; + dpy_resize(s->ds, s->last_scr_width, s->last_scr_height); + s->last_width = width; + s->last_height = height; + s->last_ch = cheight; + s->last_cw = cw; + full_update = 1; + } + cursor_offset = ((s->cr[0x0e] << 8) | s->cr[0x0f]) - s->start_addr; + if (cursor_offset != s->cursor_offset || + s->cr[0xa] != s->cursor_start || + s->cr[0xb] != s->cursor_end) { + /* if the cursor position changed, we update the old and new + chars */ + if (s->cursor_offset < CH_ATTR_SIZE) + s->last_ch_attr[s->cursor_offset] = -1; + if (cursor_offset < CH_ATTR_SIZE) + s->last_ch_attr[cursor_offset] = -1; + s->cursor_offset = cursor_offset; + s->cursor_start = s->cr[0xa]; + s->cursor_end = s->cr[0xb]; + } + cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4; + + depth_index = get_depth_index(s->ds->depth); + if (cw == 16) + vga_draw_glyph8 = vga_draw_glyph16_table[depth_index]; + else + vga_draw_glyph8 = vga_draw_glyph8_table[depth_index]; + vga_draw_glyph9 = vga_draw_glyph9_table[depth_index]; + + dest = s->ds->data; + linesize = s->ds->linesize; + ch_attr_ptr = s->last_ch_attr; + for(cy = 0; cy < height; cy++) { + d1 = dest; + src = s1; + cx_min = width; + cx_max = -1; + for(cx = 0; cx < width; cx++) { + ch_attr = *(uint16_t *)src; + if (full_update || ch_attr != *ch_attr_ptr) { + if (cx < cx_min) + cx_min = cx; + if (cx > cx_max) + cx_max = cx; + *ch_attr_ptr = ch_attr; +#ifdef WORDS_BIGENDIAN + ch = ch_attr >> 8; + cattr = ch_attr & 0xff; +#else + ch = ch_attr & 0xff; + cattr = ch_attr >> 8; +#endif + font_ptr = font_base[(cattr >> 3) & 1]; + font_ptr += 32 * 4 * ch; + bgcol = palette[cattr >> 4]; + fgcol = palette[cattr & 0x0f]; + if (cw != 9) { + vga_draw_glyph8(d1, linesize, + font_ptr, cheight, fgcol, bgcol); + } else { + dup9 = 0; + if (ch >= 0xb0 && ch <= 0xdf && (s->ar[0x10] & 0x04)) + dup9 = 1; + vga_draw_glyph9(d1, linesize, + font_ptr, cheight, fgcol, bgcol, dup9); + } + if (src == cursor_ptr && + !(s->cr[0x0a] & 0x20)) { + int line_start, line_last, h; + /* draw the cursor */ + line_start = s->cr[0x0a] & 0x1f; + line_last = s->cr[0x0b] & 0x1f; + /* XXX: check that */ + if (line_last > cheight - 1) + line_last = cheight - 1; + if (line_last >= line_start && line_start < cheight) { + h = line_last - line_start + 1; + d = d1 + linesize * line_start; + if (cw != 9) { + vga_draw_glyph8(d, linesize, + cursor_glyph, h, fgcol, bgcol); + } else { + vga_draw_glyph9(d, linesize, + cursor_glyph, h, fgcol, bgcol, 1); + } + } + } + } + d1 += x_incr; + src += 4; + ch_attr_ptr++; + } + if (cx_max != -1) { + dpy_update(s->ds, cx_min * cw, cy * cheight, + (cx_max - cx_min + 1) * cw, cheight); + } + dest += linesize * cheight; + s1 += line_offset; + } +} + +enum { + VGA_DRAW_LINE2, + VGA_DRAW_LINE2D2, + VGA_DRAW_LINE4, + VGA_DRAW_LINE4D2, + VGA_DRAW_LINE8D2, + VGA_DRAW_LINE8, + VGA_DRAW_LINE15, + VGA_DRAW_LINE16, + VGA_DRAW_LINE24, + VGA_DRAW_LINE32, + VGA_DRAW_LINE_NB, +}; + +static vga_draw_line_func *vga_draw_line_table[4 * VGA_DRAW_LINE_NB] = { + vga_draw_line2_8, + vga_draw_line2_16, + vga_draw_line2_16, + vga_draw_line2_32, + + vga_draw_line2d2_8, + vga_draw_line2d2_16, + vga_draw_line2d2_16, + vga_draw_line2d2_32, + + vga_draw_line4_8, + vga_draw_line4_16, + vga_draw_line4_16, + vga_draw_line4_32, + + vga_draw_line4d2_8, + vga_draw_line4d2_16, + vga_draw_line4d2_16, + vga_draw_line4d2_32, + + vga_draw_line8d2_8, + vga_draw_line8d2_16, + vga_draw_line8d2_16, + vga_draw_line8d2_32, + + vga_draw_line8_8, + vga_draw_line8_16, + vga_draw_line8_16, + vga_draw_line8_32, + + vga_draw_line15_8, + vga_draw_line15_15, + vga_draw_line15_16, + vga_draw_line15_32, + + vga_draw_line16_8, + vga_draw_line16_15, + vga_draw_line16_16, + vga_draw_line16_32, + + vga_draw_line24_8, + vga_draw_line24_15, + vga_draw_line24_16, + vga_draw_line24_32, + + vga_draw_line32_8, + vga_draw_line32_15, + vga_draw_line32_16, + vga_draw_line32_32, +}; + +static int vga_get_bpp(VGAState *s) +{ + int ret; +#ifdef CONFIG_BOCHS_VBE + if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { + ret = s->vbe_regs[VBE_DISPI_INDEX_BPP]; + } else +#endif + { + ret = 0; + } + return ret; +} + +static void vga_get_resolution(VGAState *s, int *pwidth, int *pheight) +{ + int width, height; + + width = (s->cr[0x01] + 1) * 8; + height = s->cr[0x12] | + ((s->cr[0x07] & 0x02) << 7) | + ((s->cr[0x07] & 0x40) << 3); + height = (height + 1); + *pwidth = width; + *pheight = height; +} + +void vga_invalidate_scanlines(VGAState *s, int y1, int y2) +{ + int y; + if (y1 >= VGA_MAX_HEIGHT) + return; + if (y2 >= VGA_MAX_HEIGHT) + y2 = VGA_MAX_HEIGHT; + for(y = y1; y < y2; y++) { + s->invalidated_y_table[y >> 5] |= 1 << (y & 0x1f); + } +} + +static inline int cmp_vram(VGAState *s, int offset, int n) +{ + long *vp, *sp; + + if (s->vram_shadow == NULL) + return 1; + vp = (long *)(s->vram_ptr + offset); + sp = (long *)(s->vram_shadow + offset); + while ((n -= sizeof(*vp)) >= 0) { + if (*vp++ != *sp++) { + memcpy(sp - 1, vp - 1, n + sizeof(*vp)); + return 1; + } + } + return 0; +} + +#ifdef USE_SSE2 + +#include +#include +#include + +int sse2_ok = 1; + +static inline unsigned int cpuid_edx(unsigned int op) +{ + unsigned int eax, edx; + +#ifdef __x86_64__ +#define __bx "rbx" +#else +#define __bx "ebx" +#endif + __asm__("push %%"__bx"; cpuid; pop %%"__bx + : "=a" (eax), "=d" (edx) + : "0" (op) + : "cx"); +#undef __bx + + return edx; +} + +jmp_buf sse_jbuf; + +void intr(int sig) +{ + sse2_ok = 0; + longjmp(sse_jbuf, 1); +} + +void check_sse2(void) +{ + /* Check 1: What does CPUID say? */ + if ((cpuid_edx(1) & 0x4000000) == 0) { + sse2_ok = 0; + return; + } + + /* Check 2: Can we use SSE2 in anger? */ + signal(SIGILL, intr); + if (setjmp(sse_jbuf) == 0) + __asm__("xorps %xmm0,%xmm0\n"); +} + +int vram_dirty(VGAState *s, int offset, int n) +{ + __m128i *sp, *vp; + + if (s->vram_shadow == NULL) + return 1; + if (sse2_ok == 0) + return cmp_vram(s, offset, n); + vp = (__m128i *)(s->vram_ptr + offset); + sp = (__m128i *)(s->vram_shadow + offset); + while ((n -= sizeof(*vp)) >= 0) { + if (_mm_movemask_epi8(_mm_cmpeq_epi8(*sp, *vp)) != 0xffff) { + while (n >= 0) { + _mm_store_si128(sp++, _mm_load_si128(vp++)); + n -= sizeof(*vp); + } + return 1; + } + sp++; + vp++; + } + return 0; +} +#else /* !USE_SSE2 */ +int vram_dirty(VGAState *s, int offset, int n) +{ + return cmp_vram(s, offset, n); +} + +void check_sse2(void) +{ +} +#endif /* !USE_SSE2 */ + +/* + * graphic modes + */ +static void vga_draw_graphic(VGAState *s, int full_update) +{ + int y1, y, update, page_min, page_max, linesize, y_start, double_scan, mask; + int width, height, shift_control, line_offset, page0, page1, bwidth; + int disp_width, multi_scan, multi_run; + uint8_t *d; + uint32_t v, addr1, addr; + vga_draw_line_func *vga_draw_line; + + full_update |= update_basic_params(s); + + s->get_resolution(s, &width, &height); + disp_width = width; + + shift_control = (s->gr[0x05] >> 5) & 3; + double_scan = (s->cr[0x09] >> 7); + if (shift_control != 1) { + multi_scan = (((s->cr[0x09] & 0x1f) + 1) << double_scan) - 1; + } else { + /* in CGA modes, multi_scan is ignored */ + /* XXX: is it correct ? */ + multi_scan = double_scan; + } + multi_run = multi_scan; + if (shift_control != s->shift_control || + double_scan != s->double_scan) { + full_update = 1; + s->shift_control = shift_control; + s->double_scan = double_scan; + } + + if (shift_control == 0) { + full_update |= update_palette16(s); + if (s->sr[0x01] & 8) { + v = VGA_DRAW_LINE4D2; + disp_width <<= 1; + } else { + v = VGA_DRAW_LINE4; + } + } else if (shift_control == 1) { + full_update |= update_palette16(s); + if (s->sr[0x01] & 8) { + v = VGA_DRAW_LINE2D2; + disp_width <<= 1; + } else { + v = VGA_DRAW_LINE2; + } + } else { + switch(s->get_bpp(s)) { + default: + case 0: + full_update |= update_palette256(s); + v = VGA_DRAW_LINE8D2; + break; + case 8: + full_update |= update_palette256(s); + v = VGA_DRAW_LINE8; + break; + case 15: + v = VGA_DRAW_LINE15; + break; + case 16: + v = VGA_DRAW_LINE16; + break; + case 24: + v = VGA_DRAW_LINE24; + break; + case 32: + v = VGA_DRAW_LINE32; + break; + } + } + vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->ds->depth)]; + + if (disp_width != s->last_width || + height != s->last_height) { + dpy_resize(s->ds, disp_width, height); + s->last_scr_width = disp_width; + s->last_scr_height = height; + s->last_width = disp_width; + s->last_height = height; + full_update = 1; + } + if (s->cursor_invalidate) + s->cursor_invalidate(s); + + line_offset = s->line_offset; +#if 0 + printf("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n", + width, height, v, line_offset, s->cr[9], s->cr[0x17], s->line_compare, s->sr[0x01]); +#endif + + for (y = 0; y < s->vram_size; y += TARGET_PAGE_SIZE) + if (vram_dirty(s, y, TARGET_PAGE_SIZE)) + cpu_physical_memory_set_dirty(s->vram_offset + y); + + addr1 = (s->start_addr * 4); + bwidth = width * 4; + y_start = -1; + page_min = 0x7fffffff; + page_max = -1; + d = s->ds->data; + linesize = s->ds->linesize; + y1 = 0; + for(y = 0; y < height; y++) { + addr = addr1; + if (!(s->cr[0x17] & 1)) { + int shift; + /* CGA compatibility handling */ + shift = 14 + ((s->cr[0x17] >> 6) & 1); + addr = (addr & ~(1 << shift)) | ((y1 & 1) << shift); + } + if (!(s->cr[0x17] & 2)) { + addr = (addr & ~0x8000) | ((y1 & 2) << 14); + } + page0 = s->vram_offset + (addr & TARGET_PAGE_MASK); + page1 = s->vram_offset + ((addr + bwidth - 1) & TARGET_PAGE_MASK); + update = full_update | + cpu_physical_memory_get_dirty(page0, VGA_DIRTY_FLAG) | + cpu_physical_memory_get_dirty(page1, VGA_DIRTY_FLAG); + if ((page1 - page0) > TARGET_PAGE_SIZE) { + /* if wide line, can use another page */ + update |= cpu_physical_memory_get_dirty(page0 + TARGET_PAGE_SIZE, + VGA_DIRTY_FLAG); + } + /* explicit invalidation for the hardware cursor */ + update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1; + if (update) { + if (y_start < 0) + y_start = y; + if (page0 < page_min) + page_min = page0; + if (page1 > page_max) + page_max = page1; + vga_draw_line(s, d, s->vram_ptr + addr, width); + if (s->cursor_draw_line) + s->cursor_draw_line(s, d, y); + } else { + if (y_start >= 0) { + /* flush to display */ + dpy_update(s->ds, 0, y_start, + disp_width, y - y_start); + y_start = -1; + } + } + if (!multi_run) { + mask = (s->cr[0x17] & 3) ^ 3; + if ((y1 & mask) == mask) + addr1 += line_offset; + y1++; + multi_run = multi_scan; + } else { + multi_run--; + } + /* line compare acts on the displayed lines */ + if (y == s->line_compare) + addr1 = 0; + d += linesize; + } + if (y_start >= 0) { + /* flush to display */ + dpy_update(s->ds, 0, y_start, + disp_width, y - y_start); + } + /* reset modified pages */ + if (page_max != -1) { + cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE, + VGA_DIRTY_FLAG); + } + memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4); +} + +static void vga_draw_blank(VGAState *s, int full_update) +{ + int i, w, val; + uint8_t *d; + + if (!full_update) + return; + if (s->last_scr_width <= 0 || s->last_scr_height <= 0) + return; + if (s->ds->depth == 8) + val = s->rgb_to_pixel(0, 0, 0); + else + val = 0; + w = s->last_scr_width * ((s->ds->depth + 7) >> 3); + d = s->ds->data; + for(i = 0; i < s->last_scr_height; i++) { + memset(d, val, w); + d += s->ds->linesize; + } + dpy_update(s->ds, 0, 0, + s->last_scr_width, s->last_scr_height); +} + +#define GMODE_TEXT 0 +#define GMODE_GRAPH 1 +#define GMODE_BLANK 2 + +static void vga_update_display(void *opaque) +{ + VGAState *s = (VGAState *)opaque; + int full_update, graphic_mode; + + if (s->ds->depth == 0) { + /* nothing to do */ + } else { + switch(s->ds->depth) { + case 8: + s->rgb_to_pixel = rgb_to_pixel8_dup; + break; + case 15: + s->rgb_to_pixel = rgb_to_pixel15_dup; + break; + default: + case 16: + s->rgb_to_pixel = rgb_to_pixel16_dup; + break; + case 32: + s->rgb_to_pixel = rgb_to_pixel32_dup; + break; + } + + full_update = 0; + if (!(s->ar_index & 0x20)) { + graphic_mode = GMODE_BLANK; + } else { + graphic_mode = s->gr[6] & 1; + } + if (graphic_mode != s->graphic_mode) { + s->graphic_mode = graphic_mode; + full_update = 1; + } + switch(graphic_mode) { + case GMODE_TEXT: + vga_draw_text(s, full_update); + break; + case GMODE_GRAPH: + vga_draw_graphic(s, full_update); + break; + case GMODE_BLANK: + default: + vga_draw_blank(s, full_update); + break; + } + } +} + +/* force a full display refresh */ +static void vga_invalidate_display(void *opaque) +{ + VGAState *s = (VGAState *)opaque; + + s->last_width = -1; + s->last_height = -1; +} + +static void vga_reset(VGAState *s) +{ + memset(s, 0, sizeof(VGAState)); + s->graphic_mode = -1; /* force full update */ +} + +static CPUReadMemoryFunc *vga_mem_read[3] = { + vga_mem_readb, + vga_mem_readw, + vga_mem_readl, +}; + +static CPUWriteMemoryFunc *vga_mem_write[3] = { + vga_mem_writeb, + vga_mem_writew, + vga_mem_writel, +}; + +static void vga_save(QEMUFile *f, void *opaque) +{ + VGAState *s = opaque; +#ifdef CONFIG_BOCHS_VBE + int i; +#endif + + qemu_put_be32s(f, &s->latch); + qemu_put_8s(f, &s->sr_index); + qemu_put_buffer(f, s->sr, 8); + qemu_put_8s(f, &s->gr_index); + qemu_put_buffer(f, s->gr, 16); + qemu_put_8s(f, &s->ar_index); + qemu_put_buffer(f, s->ar, 21); + qemu_put_be32s(f, &s->ar_flip_flop); + qemu_put_8s(f, &s->cr_index); + qemu_put_buffer(f, s->cr, 256); + qemu_put_8s(f, &s->msr); + qemu_put_8s(f, &s->fcr); + qemu_put_8s(f, &s->st00); + qemu_put_8s(f, &s->st01); + + qemu_put_8s(f, &s->dac_state); + qemu_put_8s(f, &s->dac_sub_index); + qemu_put_8s(f, &s->dac_read_index); + qemu_put_8s(f, &s->dac_write_index); + qemu_put_buffer(f, s->dac_cache, 3); + qemu_put_buffer(f, s->palette, 768); + + qemu_put_be32s(f, &s->bank_offset); +#ifdef CONFIG_BOCHS_VBE + qemu_put_byte(f, 1); + qemu_put_be16s(f, &s->vbe_index); + for(i = 0; i < VBE_DISPI_INDEX_NB; i++) + qemu_put_be16s(f, &s->vbe_regs[i]); + qemu_put_be32s(f, &s->vbe_start_addr); + qemu_put_be32s(f, &s->vbe_line_offset); + qemu_put_be32s(f, &s->vbe_bank_mask); +#else + qemu_put_byte(f, 0); +#endif +} + +static int vga_load(QEMUFile *f, void *opaque, int version_id) +{ + VGAState *s = opaque; + int is_vbe; +#ifdef CONFIG_BOCHS_VBE + int i; +#endif + + if (version_id != 1) + return -EINVAL; + + qemu_get_be32s(f, &s->latch); + qemu_get_8s(f, &s->sr_index); + qemu_get_buffer(f, s->sr, 8); + qemu_get_8s(f, &s->gr_index); + qemu_get_buffer(f, s->gr, 16); + qemu_get_8s(f, &s->ar_index); + qemu_get_buffer(f, s->ar, 21); + qemu_get_be32s(f, &s->ar_flip_flop); + qemu_get_8s(f, &s->cr_index); + qemu_get_buffer(f, s->cr, 256); + qemu_get_8s(f, &s->msr); + qemu_get_8s(f, &s->fcr); + qemu_get_8s(f, &s->st00); + qemu_get_8s(f, &s->st01); + + qemu_get_8s(f, &s->dac_state); + qemu_get_8s(f, &s->dac_sub_index); + qemu_get_8s(f, &s->dac_read_index); + qemu_get_8s(f, &s->dac_write_index); + qemu_get_buffer(f, s->dac_cache, 3); + qemu_get_buffer(f, s->palette, 768); + + qemu_get_be32s(f, &s->bank_offset); + is_vbe = qemu_get_byte(f); +#ifdef CONFIG_BOCHS_VBE + if (!is_vbe) + return -EINVAL; + qemu_get_be16s(f, &s->vbe_index); + for(i = 0; i < VBE_DISPI_INDEX_NB; i++) + qemu_get_be16s(f, &s->vbe_regs[i]); + qemu_get_be32s(f, &s->vbe_start_addr); + qemu_get_be32s(f, &s->vbe_line_offset); + qemu_get_be32s(f, &s->vbe_bank_mask); +#else + if (is_vbe) + return -EINVAL; +#endif + + /* force refresh */ + s->graphic_mode = -1; + return 0; +} + +static void vga_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + VGAState *s = vga_state; + if (region_num == PCI_ROM_SLOT) { + cpu_register_physical_memory(addr, s->bios_size, s->bios_offset); + } else { + cpu_register_physical_memory(addr, s->vram_size, s->vram_offset); + } +} + +/* when used on xen environment, the vga_ram_base is not used */ +void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base, + unsigned long vga_ram_offset, int vga_ram_size) +{ + int i, j, v, b; + + for(i = 0;i < 256; i++) { + v = 0; + for(j = 0; j < 8; j++) { + v |= ((i >> j) & 1) << (j * 4); + } + expand4[i] = v; + + v = 0; + for(j = 0; j < 4; j++) { + v |= ((i >> (2 * j)) & 3) << (j * 4); + } + expand2[i] = v; + } + for(i = 0; i < 16; i++) { + v = 0; + for(j = 0; j < 4; j++) { + b = ((i >> j) & 1); + v |= b << (2 * j); + v |= b << (2 * j + 1); + } + expand4to8[i] = v; + } + + vga_reset(s); + + check_sse2(); + s->vram_shadow = qemu_malloc(vga_ram_size+TARGET_PAGE_SIZE+1); + if (s->vram_shadow == NULL) + fprintf(stderr, "Cannot allocate %d bytes for VRAM shadow, " + "mouse will be slow\n", vga_ram_size); + s->vram_shadow = (uint8_t *)((long)(s->vram_shadow + TARGET_PAGE_SIZE - 1) + & ~(TARGET_PAGE_SIZE - 1)); + + s->vram_ptr = qemu_malloc(vga_ram_size); + s->vram_offset = vga_ram_offset; + s->vram_size = vga_ram_size; + s->ds = ds; + s->get_bpp = vga_get_bpp; + s->get_offsets = vga_get_offsets; + s->get_resolution = vga_get_resolution; + graphic_console_init(s->ds, vga_update_display, vga_invalidate_display, + vga_screen_dump, s); + /* XXX: currently needed for display */ + vga_state = s; +} + + +int vga_initialize(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, + unsigned long vga_ram_offset, int vga_ram_size, + unsigned long vga_bios_offset, int vga_bios_size) +{ + VGAState *s; + + s = qemu_mallocz(sizeof(VGAState)); + if (!s) + return -1; + + vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size); + + register_savevm("vga", 0, 1, vga_save, vga_load, s); + + register_ioport_write(0x3c0, 16, 1, vga_ioport_write, s); + + register_ioport_write(0x3b4, 2, 1, vga_ioport_write, s); + register_ioport_write(0x3d4, 2, 1, vga_ioport_write, s); + register_ioport_write(0x3ba, 1, 1, vga_ioport_write, s); + register_ioport_write(0x3da, 1, 1, vga_ioport_write, s); + + register_ioport_read(0x3c0, 16, 1, vga_ioport_read, s); + + register_ioport_read(0x3b4, 2, 1, vga_ioport_read, s); + register_ioport_read(0x3d4, 2, 1, vga_ioport_read, s); + register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s); + register_ioport_read(0x3da, 1, 1, vga_ioport_read, s); + s->bank_offset = 0; + +#ifdef CONFIG_BOCHS_VBE + s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0; + s->vbe_bank_mask = ((s->vram_size >> 16) - 1); +#if defined (TARGET_I386) + register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s); + register_ioport_read(0x1cf, 1, 2, vbe_ioport_read_data, s); + + register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s); + register_ioport_write(0x1cf, 1, 2, vbe_ioport_write_data, s); + + /* old Bochs IO ports */ + register_ioport_read(0xff80, 1, 2, vbe_ioport_read_index, s); + register_ioport_read(0xff81, 1, 2, vbe_ioport_read_data, s); + + register_ioport_write(0xff80, 1, 2, vbe_ioport_write_index, s); + register_ioport_write(0xff81, 1, 2, vbe_ioport_write_data, s); +#else + register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s); + register_ioport_read(0x1d0, 1, 2, vbe_ioport_read_data, s); + + register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s); + register_ioport_write(0x1d0, 1, 2, vbe_ioport_write_data, s); +#endif +#endif /* CONFIG_BOCHS_VBE */ + + vga_io_memory = cpu_register_io_memory(0, vga_mem_read, vga_mem_write, s); + cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000, + vga_io_memory); + + if (bus) { + PCIDevice *d; + uint8_t *pci_conf; + + d = pci_register_device(bus, "VGA", + sizeof(PCIDevice), + -1, NULL, NULL); + pci_conf = d->config; + pci_conf[0x00] = 0x34; // dummy VGA (same as Bochs ID) + pci_conf[0x01] = 0x12; + pci_conf[0x02] = 0x11; + pci_conf[0x03] = 0x11; + pci_conf[0x0a] = 0x00; // VGA controller + pci_conf[0x0b] = 0x03; + pci_conf[0x0e] = 0x00; // header_type + + /* XXX: vga_ram_size must be a power of two */ + pci_register_io_region(d, 0, vga_ram_size, + PCI_ADDRESS_SPACE_MEM_PREFETCH, vga_map); + if (vga_bios_size != 0) { + unsigned int bios_total_size; + s->bios_offset = vga_bios_offset; + s->bios_size = vga_bios_size; + /* must be a power of two */ + bios_total_size = 1; + while (bios_total_size < vga_bios_size) + bios_total_size <<= 1; + pci_register_io_region(d, PCI_ROM_SLOT, bios_total_size, + PCI_ADDRESS_SPACE_MEM_PREFETCH, vga_map); + } + } else { +#ifdef CONFIG_BOCHS_VBE + /* XXX: use optimized standard vga accesses */ + cpu_register_physical_memory(VBE_DISPI_LFB_PHYSICAL_ADDRESS, + vga_ram_size, vga_ram_offset); +#endif + } + return 0; +} + +void *vga_update_vram(VGAState *s, void *vga_ram_base, int vga_ram_size) +{ + uint8_t *old_pointer; + + if (s->vram_size != vga_ram_size) { + fprintf(stderr, "No support to change vga_ram_size\n"); + return NULL; + } + + if (!vga_ram_base) { + vga_ram_base = qemu_malloc(vga_ram_size); + if (!vga_ram_base) { + fprintf(stderr, "reallocate error\n"); + return NULL; + } + } + + /* XXX lock needed? */ + memcpy(vga_ram_base, s->vram_ptr, vga_ram_size); + old_pointer = s->vram_ptr; + s->vram_ptr = vga_ram_base; + + return old_pointer; +} + +/********************************************************/ +/* vga screen dump */ + +static int vga_save_w, vga_save_h; + +static void vga_save_dpy_update(DisplayState *s, + int x, int y, int w, int h) +{ +} + +static void vga_save_dpy_resize(DisplayState *s, int w, int h) +{ + s->linesize = w * 4; + s->data = qemu_malloc(h * s->linesize); + vga_save_w = w; + vga_save_h = h; +} + +static void vga_save_dpy_refresh(DisplayState *s) +{ +} + +static int ppm_save(const char *filename, uint8_t *data, + int w, int h, int linesize) +{ + FILE *f; + uint8_t *d, *d1; + unsigned int v; + int y, x; + + f = fopen(filename, "wb"); + if (!f) + return -1; + fprintf(f, "P6\n%d %d\n%d\n", + w, h, 255); + d1 = data; + for(y = 0; y < h; y++) { + d = d1; + for(x = 0; x < w; x++) { + v = *(uint32_t *)d; + fputc((v >> 16) & 0xff, f); + fputc((v >> 8) & 0xff, f); + fputc((v) & 0xff, f); + d += 4; + } + d1 += linesize; + } + fclose(f); + return 0; +} + +/* save the vga display in a PPM image even if no display is + available */ +static void vga_screen_dump(void *opaque, const char *filename) +{ + VGAState *s = (VGAState *)opaque; + DisplayState *saved_ds, ds1, *ds = &ds1; + + /* XXX: this is a little hackish */ + vga_invalidate_display(s); + saved_ds = s->ds; + + memset(ds, 0, sizeof(DisplayState)); + ds->dpy_update = vga_save_dpy_update; + ds->dpy_resize = vga_save_dpy_resize; + ds->dpy_refresh = vga_save_dpy_refresh; + ds->depth = 32; + + s->ds = ds; + s->graphic_mode = -1; + vga_update_display(s); + + if (ds->data) { + ppm_save(filename, ds->data, vga_save_w, vga_save_h, + s->ds->linesize); + qemu_free(ds->data); + } + s->ds = saved_ds; +} diff --git a/tools/ioemu/hw/vga_int.h b/tools/ioemu/hw/vga_int.h new file mode 100644 index 0000000000..275af653da --- /dev/null +++ b/tools/ioemu/hw/vga_int.h @@ -0,0 +1,172 @@ +/* + * QEMU internal VGA defines. + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#define MSR_COLOR_EMULATION 0x01 +#define MSR_PAGE_SELECT 0x20 + +#define ST01_V_RETRACE 0x08 +#define ST01_DISP_ENABLE 0x01 + +/* bochs VBE support */ +//#define CONFIG_BOCHS_VBE + +#define VBE_DISPI_MAX_XRES 1024 +#define VBE_DISPI_MAX_YRES 768 + +#define VBE_DISPI_INDEX_ID 0x0 +#define VBE_DISPI_INDEX_XRES 0x1 +#define VBE_DISPI_INDEX_YRES 0x2 +#define VBE_DISPI_INDEX_BPP 0x3 +#define VBE_DISPI_INDEX_ENABLE 0x4 +#define VBE_DISPI_INDEX_BANK 0x5 +#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 +#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 +#define VBE_DISPI_INDEX_X_OFFSET 0x8 +#define VBE_DISPI_INDEX_Y_OFFSET 0x9 +#define VBE_DISPI_INDEX_NB 0xa + +#define VBE_DISPI_ID0 0xB0C0 +#define VBE_DISPI_ID1 0xB0C1 +#define VBE_DISPI_ID2 0xB0C2 + +#define VBE_DISPI_DISABLED 0x00 +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_LFB_ENABLED 0x40 +#define VBE_DISPI_NOCLEARMEM 0x80 + +#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 + +#ifdef CONFIG_BOCHS_VBE + +#define VGA_STATE_COMMON_BOCHS_VBE \ + uint16_t vbe_index; \ + uint16_t vbe_regs[VBE_DISPI_INDEX_NB]; \ + uint32_t vbe_start_addr; \ + uint32_t vbe_line_offset; \ + uint32_t vbe_bank_mask; + +#else + +#define VGA_STATE_COMMON_BOCHS_VBE + +#endif /* !CONFIG_BOCHS_VBE */ + +#define CH_ATTR_SIZE (160 * 100) +#define VGA_MAX_HEIGHT 1024 + +#define VGA_STATE_COMMON \ + uint8_t *vram_ptr; \ + uint8_t *vram_shadow; \ + unsigned long vram_offset; \ + unsigned int vram_size; \ + unsigned long bios_offset; \ + unsigned int bios_size; \ + uint32_t latch; \ + uint8_t sr_index; \ + uint8_t sr[256]; \ + uint8_t gr_index; \ + uint8_t gr[256]; \ + uint8_t ar_index; \ + uint8_t ar[21]; \ + int ar_flip_flop; \ + uint8_t cr_index; \ + uint8_t cr[256]; /* CRT registers */ \ + uint8_t msr; /* Misc Output Register */ \ + uint8_t fcr; /* Feature Control Register */ \ + uint8_t st00; /* status 0 */ \ + uint8_t st01; /* status 1 */ \ + uint8_t dac_state; \ + uint8_t dac_sub_index; \ + uint8_t dac_read_index; \ + uint8_t dac_write_index; \ + uint8_t dac_cache[3]; /* used when writing */ \ + uint8_t palette[768]; \ + int32_t bank_offset; \ + int (*get_bpp)(struct VGAState *s); \ + void (*get_offsets)(struct VGAState *s, \ + uint32_t *pline_offset, \ + uint32_t *pstart_addr); \ + void (*get_resolution)(struct VGAState *s, \ + int *pwidth, \ + int *pheight); \ + VGA_STATE_COMMON_BOCHS_VBE \ + /* display refresh support */ \ + DisplayState *ds; \ + uint32_t font_offsets[2]; \ + int graphic_mode; \ + uint8_t shift_control; \ + uint8_t double_scan; \ + uint32_t line_offset; \ + uint32_t line_compare; \ + uint32_t start_addr; \ + uint32_t plane_updated; \ + uint8_t last_cw, last_ch; \ + uint32_t last_width, last_height; /* in chars or pixels */ \ + uint32_t last_scr_width, last_scr_height; /* in pixels */ \ + uint8_t cursor_start, cursor_end; \ + uint32_t cursor_offset; \ + unsigned int (*rgb_to_pixel)(unsigned int r, \ + unsigned int g, unsigned b); \ + /* hardware mouse cursor support */ \ + uint32_t invalidated_y_table[VGA_MAX_HEIGHT / 32]; \ + void (*cursor_invalidate)(struct VGAState *s); \ + void (*cursor_draw_line)(struct VGAState *s, uint8_t *d, int y); \ + /* tell for each page if it has been updated since the last time */ \ + uint32_t last_palette[256]; \ + uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */ + + +typedef struct VGAState { + VGA_STATE_COMMON +} VGAState; + +static inline int c6_to_8(int v) +{ + int b; + v &= 0x3f; + b = v & 1; + return (v << 2) | (b << 1) | b; +} + +void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base, + unsigned long vga_ram_offset, int vga_ram_size); +uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr); +void vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val); +void vga_invalidate_scanlines(VGAState *s, int y1, int y2); + +void vga_draw_cursor_line_8(uint8_t *d1, const uint8_t *src1, + int poffset, int w, + unsigned int color0, unsigned int color1, + unsigned int color_xor); +void vga_draw_cursor_line_16(uint8_t *d1, const uint8_t *src1, + int poffset, int w, + unsigned int color0, unsigned int color1, + unsigned int color_xor); +void vga_draw_cursor_line_32(uint8_t *d1, const uint8_t *src1, + int poffset, int w, + unsigned int color0, unsigned int color1, + unsigned int color_xor); + +void *vga_update_vram(VGAState *s, void *vga_ram_base, int vga_ram_size); +extern const uint8_t sr_mask[8]; +extern const uint8_t gr_mask[16]; diff --git a/tools/ioemu/hw/vga_template.h b/tools/ioemu/hw/vga_template.h new file mode 100644 index 0000000000..909571ebb3 --- /dev/null +++ b/tools/ioemu/hw/vga_template.h @@ -0,0 +1,519 @@ +/* + * QEMU VGA Emulator templates + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if DEPTH == 8 +#define BPP 1 +#define PIXEL_TYPE uint8_t +#elif DEPTH == 15 || DEPTH == 16 +#define BPP 2 +#define PIXEL_TYPE uint16_t +#elif DEPTH == 32 +#define BPP 4 +#define PIXEL_TYPE uint32_t +#else +#error unsupport depth +#endif + +#if DEPTH != 15 + +static inline void glue(vga_draw_glyph_line_, DEPTH)(uint8_t *d, + uint32_t font_data, + uint32_t xorcol, + uint32_t bgcol) +{ +#if BPP == 1 + ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol; + ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol; +#elif BPP == 2 + ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol; + ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol; + ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol; + ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol; +#else + ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol; + ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol; +#endif +} + +static void glue(vga_draw_glyph8_, DEPTH)(uint8_t *d, int linesize, + const uint8_t *font_ptr, int h, + uint32_t fgcol, uint32_t bgcol) +{ + uint32_t font_data, xorcol; + + xorcol = bgcol ^ fgcol; + do { + font_data = font_ptr[0]; + glue(vga_draw_glyph_line_, DEPTH)(d, font_data, xorcol, bgcol); + font_ptr += 4; + d += linesize; + } while (--h); +} + +static void glue(vga_draw_glyph16_, DEPTH)(uint8_t *d, int linesize, + const uint8_t *font_ptr, int h, + uint32_t fgcol, uint32_t bgcol) +{ + uint32_t font_data, xorcol; + + xorcol = bgcol ^ fgcol; + do { + font_data = font_ptr[0]; + glue(vga_draw_glyph_line_, DEPTH)(d, + expand4to8[font_data >> 4], + xorcol, bgcol); + glue(vga_draw_glyph_line_, DEPTH)(d + 8 * BPP, + expand4to8[font_data & 0x0f], + xorcol, bgcol); + font_ptr += 4; + d += linesize; + } while (--h); +} + +static void glue(vga_draw_glyph9_, DEPTH)(uint8_t *d, int linesize, + const uint8_t *font_ptr, int h, + uint32_t fgcol, uint32_t bgcol, int dup9) +{ + uint32_t font_data, xorcol, v; + + xorcol = bgcol ^ fgcol; + do { + font_data = font_ptr[0]; +#if BPP == 1 + cpu_to_32wu((uint32_t *)d, (dmask16[(font_data >> 4)] & xorcol) ^ bgcol); + v = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol; + cpu_to_32wu(((uint32_t *)d)+1, v); + if (dup9) + ((uint8_t *)d)[8] = v >> (24 * (1 - BIG)); + else + ((uint8_t *)d)[8] = bgcol; + +#elif BPP == 2 + cpu_to_32wu(((uint32_t *)d)+0, (dmask4[(font_data >> 6)] & xorcol) ^ bgcol); + cpu_to_32wu(((uint32_t *)d)+1, (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol); + cpu_to_32wu(((uint32_t *)d)+2, (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol); + v = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol; + cpu_to_32wu(((uint32_t *)d)+3, v); + if (dup9) + ((uint16_t *)d)[8] = v >> (16 * (1 - BIG)); + else + ((uint16_t *)d)[8] = bgcol; +#else + ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol; + ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol; + v = (-((font_data >> 0) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[7] = v; + if (dup9) + ((uint32_t *)d)[8] = v; + else + ((uint32_t *)d)[8] = bgcol; +#endif + font_ptr += 4; + d += linesize; + } while (--h); +} + +/* + * 4 color mode + */ +static void glue(vga_draw_line2_, DEPTH)(VGAState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + uint32_t plane_mask, *palette, data, v; + int x; + + palette = s1->last_palette; + plane_mask = mask16[s1->ar[0x12] & 0xf]; + width >>= 3; + for(x = 0; x < width; x++) { + data = ((uint32_t *)s)[0]; + data &= plane_mask; + v = expand2[GET_PLANE(data, 0)]; + v |= expand2[GET_PLANE(data, 2)] << 2; + ((PIXEL_TYPE *)d)[0] = palette[v >> 12]; + ((PIXEL_TYPE *)d)[1] = palette[(v >> 8) & 0xf]; + ((PIXEL_TYPE *)d)[2] = palette[(v >> 4) & 0xf]; + ((PIXEL_TYPE *)d)[3] = palette[(v >> 0) & 0xf]; + + v = expand2[GET_PLANE(data, 1)]; + v |= expand2[GET_PLANE(data, 3)] << 2; + ((PIXEL_TYPE *)d)[4] = palette[v >> 12]; + ((PIXEL_TYPE *)d)[5] = palette[(v >> 8) & 0xf]; + ((PIXEL_TYPE *)d)[6] = palette[(v >> 4) & 0xf]; + ((PIXEL_TYPE *)d)[7] = palette[(v >> 0) & 0xf]; + d += BPP * 8; + s += 4; + } +} + +#if BPP == 1 +#define PUT_PIXEL2(d, n, v) ((uint16_t *)d)[(n)] = (v) +#elif BPP == 2 +#define PUT_PIXEL2(d, n, v) ((uint32_t *)d)[(n)] = (v) +#else +#define PUT_PIXEL2(d, n, v) \ +((uint32_t *)d)[2*(n)] = ((uint32_t *)d)[2*(n)+1] = (v) +#endif + +/* + * 4 color mode, dup2 horizontal + */ +static void glue(vga_draw_line2d2_, DEPTH)(VGAState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + uint32_t plane_mask, *palette, data, v; + int x; + + palette = s1->last_palette; + plane_mask = mask16[s1->ar[0x12] & 0xf]; + width >>= 3; + for(x = 0; x < width; x++) { + data = ((uint32_t *)s)[0]; + data &= plane_mask; + v = expand2[GET_PLANE(data, 0)]; + v |= expand2[GET_PLANE(data, 2)] << 2; + PUT_PIXEL2(d, 0, palette[v >> 12]); + PUT_PIXEL2(d, 1, palette[(v >> 8) & 0xf]); + PUT_PIXEL2(d, 2, palette[(v >> 4) & 0xf]); + PUT_PIXEL2(d, 3, palette[(v >> 0) & 0xf]); + + v = expand2[GET_PLANE(data, 1)]; + v |= expand2[GET_PLANE(data, 3)] << 2; + PUT_PIXEL2(d, 4, palette[v >> 12]); + PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]); + PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]); + PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]); + d += BPP * 16; + s += 4; + } +} + +/* + * 16 color mode + */ +static void glue(vga_draw_line4_, DEPTH)(VGAState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + uint32_t plane_mask, data, v, *palette; + int x; + + palette = s1->last_palette; + plane_mask = mask16[s1->ar[0x12] & 0xf]; + width >>= 3; + for(x = 0; x < width; x++) { + data = ((uint32_t *)s)[0]; + data &= plane_mask; + v = expand4[GET_PLANE(data, 0)]; + v |= expand4[GET_PLANE(data, 1)] << 1; + v |= expand4[GET_PLANE(data, 2)] << 2; + v |= expand4[GET_PLANE(data, 3)] << 3; + ((PIXEL_TYPE *)d)[0] = palette[v >> 28]; + ((PIXEL_TYPE *)d)[1] = palette[(v >> 24) & 0xf]; + ((PIXEL_TYPE *)d)[2] = palette[(v >> 20) & 0xf]; + ((PIXEL_TYPE *)d)[3] = palette[(v >> 16) & 0xf]; + ((PIXEL_TYPE *)d)[4] = palette[(v >> 12) & 0xf]; + ((PIXEL_TYPE *)d)[5] = palette[(v >> 8) & 0xf]; + ((PIXEL_TYPE *)d)[6] = palette[(v >> 4) & 0xf]; + ((PIXEL_TYPE *)d)[7] = palette[(v >> 0) & 0xf]; + d += BPP * 8; + s += 4; + } +} + +/* + * 16 color mode, dup2 horizontal + */ +static void glue(vga_draw_line4d2_, DEPTH)(VGAState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + uint32_t plane_mask, data, v, *palette; + int x; + + palette = s1->last_palette; + plane_mask = mask16[s1->ar[0x12] & 0xf]; + width >>= 3; + for(x = 0; x < width; x++) { + data = ((uint32_t *)s)[0]; + data &= plane_mask; + v = expand4[GET_PLANE(data, 0)]; + v |= expand4[GET_PLANE(data, 1)] << 1; + v |= expand4[GET_PLANE(data, 2)] << 2; + v |= expand4[GET_PLANE(data, 3)] << 3; + PUT_PIXEL2(d, 0, palette[v >> 28]); + PUT_PIXEL2(d, 1, palette[(v >> 24) & 0xf]); + PUT_PIXEL2(d, 2, palette[(v >> 20) & 0xf]); + PUT_PIXEL2(d, 3, palette[(v >> 16) & 0xf]); + PUT_PIXEL2(d, 4, palette[(v >> 12) & 0xf]); + PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]); + PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]); + PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]); + d += BPP * 16; + s += 4; + } +} + +/* + * 256 color mode, double pixels + * + * XXX: add plane_mask support (never used in standard VGA modes) + */ +static void glue(vga_draw_line8d2_, DEPTH)(VGAState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + uint32_t *palette; + int x; + + palette = s1->last_palette; + width >>= 3; + for(x = 0; x < width; x++) { + PUT_PIXEL2(d, 0, palette[s[0]]); + PUT_PIXEL2(d, 1, palette[s[1]]); + PUT_PIXEL2(d, 2, palette[s[2]]); + PUT_PIXEL2(d, 3, palette[s[3]]); + d += BPP * 8; + s += 4; + } +} + +/* + * standard 256 color mode + * + * XXX: add plane_mask support (never used in standard VGA modes) + */ +static void glue(vga_draw_line8_, DEPTH)(VGAState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + uint32_t *palette; + int x; + + palette = s1->last_palette; + width >>= 3; + for(x = 0; x < width; x++) { + ((PIXEL_TYPE *)d)[0] = palette[s[0]]; + ((PIXEL_TYPE *)d)[1] = palette[s[1]]; + ((PIXEL_TYPE *)d)[2] = palette[s[2]]; + ((PIXEL_TYPE *)d)[3] = palette[s[3]]; + ((PIXEL_TYPE *)d)[4] = palette[s[4]]; + ((PIXEL_TYPE *)d)[5] = palette[s[5]]; + ((PIXEL_TYPE *)d)[6] = palette[s[6]]; + ((PIXEL_TYPE *)d)[7] = palette[s[7]]; + d += BPP * 8; + s += 8; + } +} + +#endif /* DEPTH != 15 */ + + +/* XXX: optimize */ + +/* + * 15 bit color + */ +static void glue(vga_draw_line15_, DEPTH)(VGAState *s1, uint8_t *d, + const uint8_t *s, int width) +{ +#if DEPTH == 15 && defined(WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) + memcpy(d, s, width * 2); +#else + int w; + uint32_t v, r, g, b; + + w = width; + do { + v = lduw_raw((void *)s); + r = (v >> 7) & 0xf8; + g = (v >> 2) & 0xf8; + b = (v << 3) & 0xf8; + ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); + s += 2; + d += BPP; + } while (--w != 0); +#endif +} + +/* + * 16 bit color + */ +static void glue(vga_draw_line16_, DEPTH)(VGAState *s1, uint8_t *d, + const uint8_t *s, int width) +{ +#if DEPTH == 16 && defined(WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) + memcpy(d, s, width * 2); +#else + int w; + uint32_t v, r, g, b; + + w = width; + do { + v = lduw_raw((void *)s); + r = (v >> 8) & 0xf8; + g = (v >> 3) & 0xfc; + b = (v << 3) & 0xf8; + ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); + s += 2; + d += BPP; + } while (--w != 0); +#endif +} + +/* + * 24 bit color + */ +static void glue(vga_draw_line24_, DEPTH)(VGAState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + int w; + uint32_t r, g, b; + + w = width; + do { +#if defined(TARGET_WORDS_BIGENDIAN) + r = s[0]; + g = s[1]; + b = s[2]; +#else + b = s[0]; + g = s[1]; + r = s[2]; +#endif + ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); + s += 3; + d += BPP; + } while (--w != 0); +} + +/* + * 32 bit color + */ +static void glue(vga_draw_line32_, DEPTH)(VGAState *s1, uint8_t *d, + const uint8_t *s, int width) +{ +#if DEPTH == 32 && defined(WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) + memcpy(d, s, width * 4); +#else + int w; + uint32_t r, g, b; + + w = width; + do { +#if defined(TARGET_WORDS_BIGENDIAN) + r = s[1]; + g = s[2]; + b = s[3]; +#else + b = s[0]; + g = s[1]; + r = s[2]; +#endif + ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); + s += 4; + d += BPP; + } while (--w != 0); +#endif +} + +#if DEPTH != 15 +void glue(vga_draw_cursor_line_, DEPTH)(uint8_t *d1, + const uint8_t *src1, + int poffset, int w, + unsigned int color0, + unsigned int color1, + unsigned int color_xor) +{ + const uint8_t *plane0, *plane1; + int x, b0, b1; + uint8_t *d; + + d = d1; + plane0 = src1; + plane1 = src1 + poffset; + for(x = 0; x < w; x++) { + b0 = (plane0[x >> 3] >> (7 - (x & 7))) & 1; + b1 = (plane1[x >> 3] >> (7 - (x & 7))) & 1; +#if DEPTH == 8 + switch(b0 | (b1 << 1)) { + case 0: + break; + case 1: + d[0] ^= color_xor; + break; + case 2: + d[0] = color0; + break; + case 3: + d[0] = color1; + break; + } +#elif DEPTH == 16 + switch(b0 | (b1 << 1)) { + case 0: + break; + case 1: + ((uint16_t *)d)[0] ^= color_xor; + break; + case 2: + ((uint16_t *)d)[0] = color0; + break; + case 3: + ((uint16_t *)d)[0] = color1; + break; + } +#elif DEPTH == 32 + switch(b0 | (b1 << 1)) { + case 0: + break; + case 1: + ((uint32_t *)d)[0] ^= color_xor; + break; + case 2: + ((uint32_t *)d)[0] = color0; + break; + case 3: + ((uint32_t *)d)[0] = color1; + break; + } +#else +#error unsupported depth +#endif + d += BPP; + } +} +#endif + +#undef PUT_PIXEL2 +#undef DEPTH +#undef BPP +#undef PIXEL_TYPE diff --git a/tools/ioemu/i386-dis.c b/tools/ioemu/i386-dis.c new file mode 100644 index 0000000000..0496e141d4 --- /dev/null +++ b/tools/ioemu/i386-dis.c @@ -0,0 +1,4143 @@ +/* Print i386 instructions for GDB, the GNU debugger. + Copyright 1988, 1989, 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2001 + Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* + * 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu) + * July 1988 + * modified by John Hassey (hassey@dg-rtp.dg.com) + * x86-64 support added by Jan Hubicka (jh@suse.cz) + */ + +/* + * The main tables describing the instructions is essentially a copy + * of the "Opcode Map" chapter (Appendix A) of the Intel 80386 + * Programmers Manual. Usually, there is a capital letter, followed + * by a small letter. The capital letter tell the addressing mode, + * and the small letter tells about the operand size. Refer to + * the Intel manual for details. + */ + +#include +#include "dis-asm.h" + +#define MAXLEN 20 + +#include + +#ifndef UNIXWARE_COMPAT +/* Set non-zero for broken, compatible instructions. Set to zero for + non-broken opcodes. */ +#define UNIXWARE_COMPAT 1 +#endif + +static int fetch_data PARAMS ((struct disassemble_info *, bfd_byte *)); +static void ckprefix PARAMS ((void)); +static const char *prefix_name PARAMS ((int, int)); +static int print_insn PARAMS ((bfd_vma, disassemble_info *)); +static void dofloat PARAMS ((int)); +static void OP_ST PARAMS ((int, int)); +static void OP_STi PARAMS ((int, int)); +static int putop PARAMS ((const char *, int)); +static void oappend PARAMS ((const char *)); +static void append_seg PARAMS ((void)); +static void OP_indirE PARAMS ((int, int)); +static void print_operand_value PARAMS ((char *, int, bfd_vma)); +static void OP_E PARAMS ((int, int)); +static void OP_G PARAMS ((int, int)); +static bfd_vma get64 PARAMS ((void)); +static bfd_signed_vma get32 PARAMS ((void)); +static bfd_signed_vma get32s PARAMS ((void)); +static int get16 PARAMS ((void)); +static void set_op PARAMS ((bfd_vma, int)); +static void OP_REG PARAMS ((int, int)); +static void OP_IMREG PARAMS ((int, int)); +static void OP_I PARAMS ((int, int)); +static void OP_I64 PARAMS ((int, int)); +static void OP_sI PARAMS ((int, int)); +static void OP_J PARAMS ((int, int)); +static void OP_SEG PARAMS ((int, int)); +static void OP_DIR PARAMS ((int, int)); +static void OP_OFF PARAMS ((int, int)); +static void OP_OFF64 PARAMS ((int, int)); +static void ptr_reg PARAMS ((int, int)); +static void OP_ESreg PARAMS ((int, int)); +static void OP_DSreg PARAMS ((int, int)); +static void OP_C PARAMS ((int, int)); +static void OP_D PARAMS ((int, int)); +static void OP_T PARAMS ((int, int)); +static void OP_Rd PARAMS ((int, int)); +static void OP_MMX PARAMS ((int, int)); +static void OP_XMM PARAMS ((int, int)); +static void OP_EM PARAMS ((int, int)); +static void OP_EX PARAMS ((int, int)); +static void OP_MS PARAMS ((int, int)); +static void OP_XS PARAMS ((int, int)); +static void OP_3DNowSuffix PARAMS ((int, int)); +static void OP_SIMD_Suffix PARAMS ((int, int)); +static void SIMD_Fixup PARAMS ((int, int)); +static void BadOp PARAMS ((void)); + +struct dis_private { + /* Points to first byte not fetched. */ + bfd_byte *max_fetched; + bfd_byte the_buffer[MAXLEN]; + bfd_vma insn_start; + int orig_sizeflag; + jmp_buf bailout; +}; + +/* The opcode for the fwait instruction, which we treat as a prefix + when we can. */ +#define FWAIT_OPCODE (0x9b) + +/* Set to 1 for 64bit mode disassembly. */ +static int mode_64bit; + +/* Flags for the prefixes for the current instruction. See below. */ +static int prefixes; + +/* REX prefix the current instruction. See below. */ +static int rex; +/* Bits of REX we've already used. */ +static int rex_used; +#define REX_MODE64 8 +#define REX_EXTX 4 +#define REX_EXTY 2 +#define REX_EXTZ 1 +/* Mark parts used in the REX prefix. When we are testing for + empty prefix (for 8bit register REX extension), just mask it + out. Otherwise test for REX bit is excuse for existence of REX + only in case value is nonzero. */ +#define USED_REX(value) \ + { \ + if (value) \ + rex_used |= (rex & value) ? (value) | 0x40 : 0; \ + else \ + rex_used |= 0x40; \ + } + +/* Flags for prefixes which we somehow handled when printing the + current instruction. */ +static int used_prefixes; + +/* Flags stored in PREFIXES. */ +#define PREFIX_REPZ 1 +#define PREFIX_REPNZ 2 +#define PREFIX_LOCK 4 +#define PREFIX_CS 8 +#define PREFIX_SS 0x10 +#define PREFIX_DS 0x20 +#define PREFIX_ES 0x40 +#define PREFIX_FS 0x80 +#define PREFIX_GS 0x100 +#define PREFIX_DATA 0x200 +#define PREFIX_ADDR 0x400 +#define PREFIX_FWAIT 0x800 + +/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive) + to ADDR (exclusive) are valid. Returns 1 for success, longjmps + on error. */ +#define FETCH_DATA(info, addr) \ + ((addr) <= ((struct dis_private *) (info->private_data))->max_fetched \ + ? 1 : fetch_data ((info), (addr))) + +static int +fetch_data (info, addr) + struct disassemble_info *info; + bfd_byte *addr; +{ + int status; + struct dis_private *priv = (struct dis_private *) info->private_data; + bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer); + + status = (*info->read_memory_func) (start, + priv->max_fetched, + addr - priv->max_fetched, + info); + if (status != 0) + { + /* If we did manage to read at least one byte, then + print_insn_i386 will do something sensible. Otherwise, print + an error. We do that here because this is where we know + STATUS. */ + if (priv->max_fetched == priv->the_buffer) + (*info->memory_error_func) (status, start, info); + longjmp (priv->bailout, 1); + } + else + priv->max_fetched = addr; + return 1; +} + +#define XX NULL, 0 + +#define Eb OP_E, b_mode +#define Ev OP_E, v_mode +#define Ed OP_E, d_mode +#define indirEb OP_indirE, b_mode +#define indirEv OP_indirE, v_mode +#define Ew OP_E, w_mode +#define Ma OP_E, v_mode +#define M OP_E, 0 /* lea, lgdt, etc. */ +#define Mp OP_E, 0 /* 32 or 48 bit memory operand for LDS, LES etc */ +#define Gb OP_G, b_mode +#define Gv OP_G, v_mode +#define Gd OP_G, d_mode +#define Gw OP_G, w_mode +#define Rd OP_Rd, d_mode +#define Rm OP_Rd, m_mode +#define Ib OP_I, b_mode +#define sIb OP_sI, b_mode /* sign extened byte */ +#define Iv OP_I, v_mode +#define Iq OP_I, q_mode +#define Iv64 OP_I64, v_mode +#define Iw OP_I, w_mode +#define Jb OP_J, b_mode +#define Jv OP_J, v_mode +#define Cm OP_C, m_mode +#define Dm OP_D, m_mode +#define Td OP_T, d_mode + +#define RMeAX OP_REG, eAX_reg +#define RMeBX OP_REG, eBX_reg +#define RMeCX OP_REG, eCX_reg +#define RMeDX OP_REG, eDX_reg +#define RMeSP OP_REG, eSP_reg +#define RMeBP OP_REG, eBP_reg +#define RMeSI OP_REG, eSI_reg +#define RMeDI OP_REG, eDI_reg +#define RMrAX OP_REG, rAX_reg +#define RMrBX OP_REG, rBX_reg +#define RMrCX OP_REG, rCX_reg +#define RMrDX OP_REG, rDX_reg +#define RMrSP OP_REG, rSP_reg +#define RMrBP OP_REG, rBP_reg +#define RMrSI OP_REG, rSI_reg +#define RMrDI OP_REG, rDI_reg +#define RMAL OP_REG, al_reg +#define RMAL OP_REG, al_reg +#define RMCL OP_REG, cl_reg +#define RMDL OP_REG, dl_reg +#define RMBL OP_REG, bl_reg +#define RMAH OP_REG, ah_reg +#define RMCH OP_REG, ch_reg +#define RMDH OP_REG, dh_reg +#define RMBH OP_REG, bh_reg +#define RMAX OP_REG, ax_reg +#define RMDX OP_REG, dx_reg + +#define eAX OP_IMREG, eAX_reg +#define eBX OP_IMREG, eBX_reg +#define eCX OP_IMREG, eCX_reg +#define eDX OP_IMREG, eDX_reg +#define eSP OP_IMREG, eSP_reg +#define eBP OP_IMREG, eBP_reg +#define eSI OP_IMREG, eSI_reg +#define eDI OP_IMREG, eDI_reg +#define AL OP_IMREG, al_reg +#define AL OP_IMREG, al_reg +#define CL OP_IMREG, cl_reg +#define DL OP_IMREG, dl_reg +#define BL OP_IMREG, bl_reg +#define AH OP_IMREG, ah_reg +#define CH OP_IMREG, ch_reg +#define DH OP_IMREG, dh_reg +#define BH OP_IMREG, bh_reg +#define AX OP_IMREG, ax_reg +#define DX OP_IMREG, dx_reg +#define indirDX OP_IMREG, indir_dx_reg + +#define Sw OP_SEG, w_mode +#define Ap OP_DIR, 0 +#define Ob OP_OFF, b_mode +#define Ob64 OP_OFF64, b_mode +#define Ov OP_OFF, v_mode +#define Ov64 OP_OFF64, v_mode +#define Xb OP_DSreg, eSI_reg +#define Xv OP_DSreg, eSI_reg +#define Yb OP_ESreg, eDI_reg +#define Yv OP_ESreg, eDI_reg +#define DSBX OP_DSreg, eBX_reg + +#define es OP_REG, es_reg +#define ss OP_REG, ss_reg +#define cs OP_REG, cs_reg +#define ds OP_REG, ds_reg +#define fs OP_REG, fs_reg +#define gs OP_REG, gs_reg + +#define MX OP_MMX, 0 +#define XM OP_XMM, 0 +#define EM OP_EM, v_mode +#define EX OP_EX, v_mode +#define MS OP_MS, v_mode +#define XS OP_XS, v_mode +#define None OP_E, 0 +#define OPSUF OP_3DNowSuffix, 0 +#define OPSIMD OP_SIMD_Suffix, 0 + +#define cond_jump_flag NULL, cond_jump_mode +#define loop_jcxz_flag NULL, loop_jcxz_mode + +/* bits in sizeflag */ +#define SUFFIX_ALWAYS 4 +#define AFLAG 2 +#define DFLAG 1 + +#define b_mode 1 /* byte operand */ +#define v_mode 2 /* operand size depends on prefixes */ +#define w_mode 3 /* word operand */ +#define d_mode 4 /* double word operand */ +#define q_mode 5 /* quad word operand */ +#define x_mode 6 +#define m_mode 7 /* d_mode in 32bit, q_mode in 64bit mode. */ +#define cond_jump_mode 8 +#define loop_jcxz_mode 9 + +#define es_reg 100 +#define cs_reg 101 +#define ss_reg 102 +#define ds_reg 103 +#define fs_reg 104 +#define gs_reg 105 + +#define eAX_reg 108 +#define eCX_reg 109 +#define eDX_reg 110 +#define eBX_reg 111 +#define eSP_reg 112 +#define eBP_reg 113 +#define eSI_reg 114 +#define eDI_reg 115 + +#define al_reg 116 +#define cl_reg 117 +#define dl_reg 118 +#define bl_reg 119 +#define ah_reg 120 +#define ch_reg 121 +#define dh_reg 122 +#define bh_reg 123 + +#define ax_reg 124 +#define cx_reg 125 +#define dx_reg 126 +#define bx_reg 127 +#define sp_reg 128 +#define bp_reg 129 +#define si_reg 130 +#define di_reg 131 + +#define rAX_reg 132 +#define rCX_reg 133 +#define rDX_reg 134 +#define rBX_reg 135 +#define rSP_reg 136 +#define rBP_reg 137 +#define rSI_reg 138 +#define rDI_reg 139 + +#define indir_dx_reg 150 + +#define FLOATCODE 1 +#define USE_GROUPS 2 +#define USE_PREFIX_USER_TABLE 3 +#define X86_64_SPECIAL 4 + +#define FLOAT NULL, NULL, FLOATCODE, NULL, 0, NULL, 0 + +#define GRP1b NULL, NULL, USE_GROUPS, NULL, 0, NULL, 0 +#define GRP1S NULL, NULL, USE_GROUPS, NULL, 1, NULL, 0 +#define GRP1Ss NULL, NULL, USE_GROUPS, NULL, 2, NULL, 0 +#define GRP2b NULL, NULL, USE_GROUPS, NULL, 3, NULL, 0 +#define GRP2S NULL, NULL, USE_GROUPS, NULL, 4, NULL, 0 +#define GRP2b_one NULL, NULL, USE_GROUPS, NULL, 5, NULL, 0 +#define GRP2S_one NULL, NULL, USE_GROUPS, NULL, 6, NULL, 0 +#define GRP2b_cl NULL, NULL, USE_GROUPS, NULL, 7, NULL, 0 +#define GRP2S_cl NULL, NULL, USE_GROUPS, NULL, 8, NULL, 0 +#define GRP3b NULL, NULL, USE_GROUPS, NULL, 9, NULL, 0 +#define GRP3S NULL, NULL, USE_GROUPS, NULL, 10, NULL, 0 +#define GRP4 NULL, NULL, USE_GROUPS, NULL, 11, NULL, 0 +#define GRP5 NULL, NULL, USE_GROUPS, NULL, 12, NULL, 0 +#define GRP6 NULL, NULL, USE_GROUPS, NULL, 13, NULL, 0 +#define GRP7 NULL, NULL, USE_GROUPS, NULL, 14, NULL, 0 +#define GRP8 NULL, NULL, USE_GROUPS, NULL, 15, NULL, 0 +#define GRP9 NULL, NULL, USE_GROUPS, NULL, 16, NULL, 0 +#define GRP10 NULL, NULL, USE_GROUPS, NULL, 17, NULL, 0 +#define GRP11 NULL, NULL, USE_GROUPS, NULL, 18, NULL, 0 +#define GRP12 NULL, NULL, USE_GROUPS, NULL, 19, NULL, 0 +#define GRP13 NULL, NULL, USE_GROUPS, NULL, 20, NULL, 0 +#define GRP14 NULL, NULL, USE_GROUPS, NULL, 21, NULL, 0 +#define GRPAMD NULL, NULL, USE_GROUPS, NULL, 22, NULL, 0 + +#define PREGRP0 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 0, NULL, 0 +#define PREGRP1 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 1, NULL, 0 +#define PREGRP2 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 2, NULL, 0 +#define PREGRP3 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 3, NULL, 0 +#define PREGRP4 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 4, NULL, 0 +#define PREGRP5 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 5, NULL, 0 +#define PREGRP6 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 6, NULL, 0 +#define PREGRP7 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 7, NULL, 0 +#define PREGRP8 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 8, NULL, 0 +#define PREGRP9 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 9, NULL, 0 +#define PREGRP10 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 10, NULL, 0 +#define PREGRP11 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 11, NULL, 0 +#define PREGRP12 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 12, NULL, 0 +#define PREGRP13 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 13, NULL, 0 +#define PREGRP14 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 14, NULL, 0 +#define PREGRP15 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 15, NULL, 0 +#define PREGRP16 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 16, NULL, 0 +#define PREGRP17 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 17, NULL, 0 +#define PREGRP18 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 18, NULL, 0 +#define PREGRP19 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 19, NULL, 0 +#define PREGRP20 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 20, NULL, 0 +#define PREGRP21 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 21, NULL, 0 +#define PREGRP22 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 22, NULL, 0 +#define PREGRP23 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 23, NULL, 0 +#define PREGRP24 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 24, NULL, 0 +#define PREGRP25 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 25, NULL, 0 +#define PREGRP26 NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 26, NULL, 0 + +#define X86_64_0 NULL, NULL, X86_64_SPECIAL, NULL, 0, NULL, 0 + +typedef void (*op_rtn) PARAMS ((int bytemode, int sizeflag)); + +struct dis386 { + const char *name; + op_rtn op1; + int bytemode1; + op_rtn op2; + int bytemode2; + op_rtn op3; + int bytemode3; +}; + +/* Upper case letters in the instruction names here are macros. + 'A' => print 'b' if no register operands or suffix_always is true + 'B' => print 'b' if suffix_always is true + 'E' => print 'e' if 32-bit form of jcxz + 'F' => print 'w' or 'l' depending on address size prefix (loop insns) + 'H' => print ",pt" or ",pn" branch hint + 'L' => print 'l' if suffix_always is true + 'N' => print 'n' if instruction has no wait "prefix" + 'O' => print 'd', or 'o' + 'P' => print 'w', 'l' or 'q' if instruction has an operand size prefix, + . or suffix_always is true. print 'q' if rex prefix is present. + 'Q' => print 'w', 'l' or 'q' if no register operands or suffix_always + . is true + 'R' => print 'w', 'l' or 'q' ("wd" or "dq" in intel mode) + 'S' => print 'w', 'l' or 'q' if suffix_always is true + 'T' => print 'q' in 64bit mode and behave as 'P' otherwise + 'U' => print 'q' in 64bit mode and behave as 'Q' otherwise + 'X' => print 's', 'd' depending on data16 prefix (for XMM) + 'W' => print 'b' or 'w' ("w" or "de" in intel mode) + 'Y' => 'q' if instruction has an REX 64bit overwrite prefix + + Many of the above letters print nothing in Intel mode. See "putop" + for the details. + + Braces '{' and '}', and vertical bars '|', indicate alternative + mnemonic strings for AT&T, Intel, X86_64 AT&T, and X86_64 Intel + modes. In cases where there are only two alternatives, the X86_64 + instruction is reserved, and "(bad)" is printed. +*/ + +static const struct dis386 dis386[] = { + /* 00 */ + { "addB", Eb, Gb, XX }, + { "addS", Ev, Gv, XX }, + { "addB", Gb, Eb, XX }, + { "addS", Gv, Ev, XX }, + { "addB", AL, Ib, XX }, + { "addS", eAX, Iv, XX }, + { "push{T|}", es, XX, XX }, + { "pop{T|}", es, XX, XX }, + /* 08 */ + { "orB", Eb, Gb, XX }, + { "orS", Ev, Gv, XX }, + { "orB", Gb, Eb, XX }, + { "orS", Gv, Ev, XX }, + { "orB", AL, Ib, XX }, + { "orS", eAX, Iv, XX }, + { "push{T|}", cs, XX, XX }, + { "(bad)", XX, XX, XX }, /* 0x0f extended opcode escape */ + /* 10 */ + { "adcB", Eb, Gb, XX }, + { "adcS", Ev, Gv, XX }, + { "adcB", Gb, Eb, XX }, + { "adcS", Gv, Ev, XX }, + { "adcB", AL, Ib, XX }, + { "adcS", eAX, Iv, XX }, + { "push{T|}", ss, XX, XX }, + { "popT|}", ss, XX, XX }, + /* 18 */ + { "sbbB", Eb, Gb, XX }, + { "sbbS", Ev, Gv, XX }, + { "sbbB", Gb, Eb, XX }, + { "sbbS", Gv, Ev, XX }, + { "sbbB", AL, Ib, XX }, + { "sbbS", eAX, Iv, XX }, + { "push{T|}", ds, XX, XX }, + { "pop{T|}", ds, XX, XX }, + /* 20 */ + { "andB", Eb, Gb, XX }, + { "andS", Ev, Gv, XX }, + { "andB", Gb, Eb, XX }, + { "andS", Gv, Ev, XX }, + { "andB", AL, Ib, XX }, + { "andS", eAX, Iv, XX }, + { "(bad)", XX, XX, XX }, /* SEG ES prefix */ + { "daa{|}", XX, XX, XX }, + /* 28 */ + { "subB", Eb, Gb, XX }, + { "subS", Ev, Gv, XX }, + { "subB", Gb, Eb, XX }, + { "subS", Gv, Ev, XX }, + { "subB", AL, Ib, XX }, + { "subS", eAX, Iv, XX }, + { "(bad)", XX, XX, XX }, /* SEG CS prefix */ + { "das{|}", XX, XX, XX }, + /* 30 */ + { "xorB", Eb, Gb, XX }, + { "xorS", Ev, Gv, XX }, + { "xorB", Gb, Eb, XX }, + { "xorS", Gv, Ev, XX }, + { "xorB", AL, Ib, XX }, + { "xorS", eAX, Iv, XX }, + { "(bad)", XX, XX, XX }, /* SEG SS prefix */ + { "aaa{|}", XX, XX, XX }, + /* 38 */ + { "cmpB", Eb, Gb, XX }, + { "cmpS", Ev, Gv, XX }, + { "cmpB", Gb, Eb, XX }, + { "cmpS", Gv, Ev, XX }, + { "cmpB", AL, Ib, XX }, + { "cmpS", eAX, Iv, XX }, + { "(bad)", XX, XX, XX }, /* SEG DS prefix */ + { "aas{|}", XX, XX, XX }, + /* 40 */ + { "inc{S|}", RMeAX, XX, XX }, + { "inc{S|}", RMeCX, XX, XX }, + { "inc{S|}", RMeDX, XX, XX }, + { "inc{S|}", RMeBX, XX, XX }, + { "inc{S|}", RMeSP, XX, XX }, + { "inc{S|}", RMeBP, XX, XX }, + { "inc{S|}", RMeSI, XX, XX }, + { "inc{S|}", RMeDI, XX, XX }, + /* 48 */ + { "dec{S|}", RMeAX, XX, XX }, + { "dec{S|}", RMeCX, XX, XX }, + { "dec{S|}", RMeDX, XX, XX }, + { "dec{S|}", RMeBX, XX, XX }, + { "dec{S|}", RMeSP, XX, XX }, + { "dec{S|}", RMeBP, XX, XX }, + { "dec{S|}", RMeSI, XX, XX }, + { "dec{S|}", RMeDI, XX, XX }, + /* 50 */ + { "pushS", RMrAX, XX, XX }, + { "pushS", RMrCX, XX, XX }, + { "pushS", RMrDX, XX, XX }, + { "pushS", RMrBX, XX, XX }, + { "pushS", RMrSP, XX, XX }, + { "pushS", RMrBP, XX, XX }, + { "pushS", RMrSI, XX, XX }, + { "pushS", RMrDI, XX, XX }, + /* 58 */ + { "popS", RMrAX, XX, XX }, + { "popS", RMrCX, XX, XX }, + { "popS", RMrDX, XX, XX }, + { "popS", RMrBX, XX, XX }, + { "popS", RMrSP, XX, XX }, + { "popS", RMrBP, XX, XX }, + { "popS", RMrSI, XX, XX }, + { "popS", RMrDI, XX, XX }, + /* 60 */ + { "pusha{P|}", XX, XX, XX }, + { "popa{P|}", XX, XX, XX }, + { "bound{S|}", Gv, Ma, XX }, + { X86_64_0 }, + { "(bad)", XX, XX, XX }, /* seg fs */ + { "(bad)", XX, XX, XX }, /* seg gs */ + { "(bad)", XX, XX, XX }, /* op size prefix */ + { "(bad)", XX, XX, XX }, /* adr size prefix */ + /* 68 */ + { "pushT", Iq, XX, XX }, + { "imulS", Gv, Ev, Iv }, + { "pushT", sIb, XX, XX }, + { "imulS", Gv, Ev, sIb }, + { "ins{b||b|}", Yb, indirDX, XX }, + { "ins{R||R|}", Yv, indirDX, XX }, + { "outs{b||b|}", indirDX, Xb, XX }, + { "outs{R||R|}", indirDX, Xv, XX }, + /* 70 */ + { "joH", Jb, XX, cond_jump_flag }, + { "jnoH", Jb, XX, cond_jump_flag }, + { "jbH", Jb, XX, cond_jump_flag }, + { "jaeH", Jb, XX, cond_jump_flag }, + { "jeH", Jb, XX, cond_jump_flag }, + { "jneH", Jb, XX, cond_jump_flag }, + { "jbeH", Jb, XX, cond_jump_flag }, + { "jaH", Jb, XX, cond_jump_flag }, + /* 78 */ + { "jsH", Jb, XX, cond_jump_flag }, + { "jnsH", Jb, XX, cond_jump_flag }, + { "jpH", Jb, XX, cond_jump_flag }, + { "jnpH", Jb, XX, cond_jump_flag }, + { "jlH", Jb, XX, cond_jump_flag }, + { "jgeH", Jb, XX, cond_jump_flag }, + { "jleH", Jb, XX, cond_jump_flag }, + { "jgH", Jb, XX, cond_jump_flag }, + /* 80 */ + { GRP1b }, + { GRP1S }, + { "(bad)", XX, XX, XX }, + { GRP1Ss }, + { "testB", Eb, Gb, XX }, + { "testS", Ev, Gv, XX }, + { "xchgB", Eb, Gb, XX }, + { "xchgS", Ev, Gv, XX }, + /* 88 */ + { "movB", Eb, Gb, XX }, + { "movS", Ev, Gv, XX }, + { "movB", Gb, Eb, XX }, + { "movS", Gv, Ev, XX }, + { "movQ", Ev, Sw, XX }, + { "leaS", Gv, M, XX }, + { "movQ", Sw, Ev, XX }, + { "popU", Ev, XX, XX }, + /* 90 */ + { "nop", XX, XX, XX }, + /* FIXME: NOP with REPz prefix is called PAUSE. */ + { "xchgS", RMeCX, eAX, XX }, + { "xchgS", RMeDX, eAX, XX }, + { "xchgS", RMeBX, eAX, XX }, + { "xchgS", RMeSP, eAX, XX }, + { "xchgS", RMeBP, eAX, XX }, + { "xchgS", RMeSI, eAX, XX }, + { "xchgS", RMeDI, eAX, XX }, + /* 98 */ + { "cW{tR||tR|}", XX, XX, XX }, + { "cR{tO||tO|}", XX, XX, XX }, + { "lcall{T|}", Ap, XX, XX }, + { "(bad)", XX, XX, XX }, /* fwait */ + { "pushfT", XX, XX, XX }, + { "popfT", XX, XX, XX }, + { "sahf{|}", XX, XX, XX }, + { "lahf{|}", XX, XX, XX }, + /* a0 */ + { "movB", AL, Ob64, XX }, + { "movS", eAX, Ov64, XX }, + { "movB", Ob64, AL, XX }, + { "movS", Ov64, eAX, XX }, + { "movs{b||b|}", Yb, Xb, XX }, + { "movs{R||R|}", Yv, Xv, XX }, + { "cmps{b||b|}", Xb, Yb, XX }, + { "cmps{R||R|}", Xv, Yv, XX }, + /* a8 */ + { "testB", AL, Ib, XX }, + { "testS", eAX, Iv, XX }, + { "stosB", Yb, AL, XX }, + { "stosS", Yv, eAX, XX }, + { "lodsB", AL, Xb, XX }, + { "lodsS", eAX, Xv, XX }, + { "scasB", AL, Yb, XX }, + { "scasS", eAX, Yv, XX }, + /* b0 */ + { "movB", RMAL, Ib, XX }, + { "movB", RMCL, Ib, XX }, + { "movB", RMDL, Ib, XX }, + { "movB", RMBL, Ib, XX }, + { "movB", RMAH, Ib, XX }, + { "movB", RMCH, Ib, XX }, + { "movB", RMDH, Ib, XX }, + { "movB", RMBH, Ib, XX }, + /* b8 */ + { "movS", RMeAX, Iv64, XX }, + { "movS", RMeCX, Iv64, XX }, + { "movS", RMeDX, Iv64, XX }, + { "movS", RMeBX, Iv64, XX }, + { "movS", RMeSP, Iv64, XX }, + { "movS", RMeBP, Iv64, XX }, + { "movS", RMeSI, Iv64, XX }, + { "movS", RMeDI, Iv64, XX }, + /* c0 */ + { GRP2b }, + { GRP2S }, + { "retT", Iw, XX, XX }, + { "retT", XX, XX, XX }, + { "les{S|}", Gv, Mp, XX }, + { "ldsS", Gv, Mp, XX }, + { "movA", Eb, Ib, XX }, + { "movQ", Ev, Iv, XX }, + /* c8 */ + { "enterT", Iw, Ib, XX }, + { "leaveT", XX, XX, XX }, + { "lretP", Iw, XX, XX }, + { "lretP", XX, XX, XX }, + { "int3", XX, XX, XX }, + { "int", Ib, XX, XX }, + { "into{|}", XX, XX, XX }, + { "iretP", XX, XX, XX }, + /* d0 */ + { GRP2b_one }, + { GRP2S_one }, + { GRP2b_cl }, + { GRP2S_cl }, + { "aam{|}", sIb, XX, XX }, + { "aad{|}", sIb, XX, XX }, + { "(bad)", XX, XX, XX }, + { "xlat", DSBX, XX, XX }, + /* d8 */ + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + /* e0 */ + { "loopneFH", Jb, XX, loop_jcxz_flag }, + { "loopeFH", Jb, XX, loop_jcxz_flag }, + { "loopFH", Jb, XX, loop_jcxz_flag }, + { "jEcxzH", Jb, XX, loop_jcxz_flag }, + { "inB", AL, Ib, XX }, + { "inS", eAX, Ib, XX }, + { "outB", Ib, AL, XX }, + { "outS", Ib, eAX, XX }, + /* e8 */ + { "callT", Jv, XX, XX }, + { "jmpT", Jv, XX, XX }, + { "ljmp{T|}", Ap, XX, XX }, + { "jmp", Jb, XX, XX }, + { "inB", AL, indirDX, XX }, + { "inS", eAX, indirDX, XX }, + { "outB", indirDX, AL, XX }, + { "outS", indirDX, eAX, XX }, + /* f0 */ + { "(bad)", XX, XX, XX }, /* lock prefix */ + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, /* repne */ + { "(bad)", XX, XX, XX }, /* repz */ + { "hlt", XX, XX, XX }, + { "cmc", XX, XX, XX }, + { GRP3b }, + { GRP3S }, + /* f8 */ + { "clc", XX, XX, XX }, + { "stc", XX, XX, XX }, + { "cli", XX, XX, XX }, + { "sti", XX, XX, XX }, + { "cld", XX, XX, XX }, + { "std", XX, XX, XX }, + { GRP4 }, + { GRP5 }, +}; + +static const struct dis386 dis386_twobyte[] = { + /* 00 */ + { GRP6 }, + { GRP7 }, + { "larS", Gv, Ew, XX }, + { "lslS", Gv, Ew, XX }, + { "(bad)", XX, XX, XX }, + { "syscall", XX, XX, XX }, + { "clts", XX, XX, XX }, + { "sysretP", XX, XX, XX }, + /* 08 */ + { "invd", XX, XX, XX }, + { "wbinvd", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "ud2a", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { GRPAMD }, + { "femms", XX, XX, XX }, + { "", MX, EM, OPSUF }, /* See OP_3DNowSuffix. */ + /* 10 */ + { PREGRP8 }, + { PREGRP9 }, + { "movlpX", XM, EX, SIMD_Fixup, 'h' }, /* really only 2 operands */ + { "movlpX", EX, XM, SIMD_Fixup, 'h' }, + { "unpcklpX", XM, EX, XX }, + { "unpckhpX", XM, EX, XX }, + { "movhpX", XM, EX, SIMD_Fixup, 'l' }, + { "movhpX", EX, XM, SIMD_Fixup, 'l' }, + /* 18 */ + { GRP14 }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* 20 */ + { "movL", Rm, Cm, XX }, + { "movL", Rm, Dm, XX }, + { "movL", Cm, Rm, XX }, + { "movL", Dm, Rm, XX }, + { "movL", Rd, Td, XX }, + { "(bad)", XX, XX, XX }, + { "movL", Td, Rd, XX }, + { "(bad)", XX, XX, XX }, + /* 28 */ + { "movapX", XM, EX, XX }, + { "movapX", EX, XM, XX }, + { PREGRP2 }, + { "movntpX", Ev, XM, XX }, + { PREGRP4 }, + { PREGRP3 }, + { "ucomisX", XM,EX, XX }, + { "comisX", XM,EX, XX }, + /* 30 */ + { "wrmsr", XX, XX, XX }, + { "rdtsc", XX, XX, XX }, + { "rdmsr", XX, XX, XX }, + { "rdpmc", XX, XX, XX }, + { "sysenter", XX, XX, XX }, + { "sysexit", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* 38 */ + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* 40 */ + { "cmovo", Gv, Ev, XX }, + { "cmovno", Gv, Ev, XX }, + { "cmovb", Gv, Ev, XX }, + { "cmovae", Gv, Ev, XX }, + { "cmove", Gv, Ev, XX }, + { "cmovne", Gv, Ev, XX }, + { "cmovbe", Gv, Ev, XX }, + { "cmova", Gv, Ev, XX }, + /* 48 */ + { "cmovs", Gv, Ev, XX }, + { "cmovns", Gv, Ev, XX }, + { "cmovp", Gv, Ev, XX }, + { "cmovnp", Gv, Ev, XX }, + { "cmovl", Gv, Ev, XX }, + { "cmovge", Gv, Ev, XX }, + { "cmovle", Gv, Ev, XX }, + { "cmovg", Gv, Ev, XX }, + /* 50 */ + { "movmskpX", Gd, XS, XX }, + { PREGRP13 }, + { PREGRP12 }, + { PREGRP11 }, + { "andpX", XM, EX, XX }, + { "andnpX", XM, EX, XX }, + { "orpX", XM, EX, XX }, + { "xorpX", XM, EX, XX }, + /* 58 */ + { PREGRP0 }, + { PREGRP10 }, + { PREGRP17 }, + { PREGRP16 }, + { PREGRP14 }, + { PREGRP7 }, + { PREGRP5 }, + { PREGRP6 }, + /* 60 */ + { "punpcklbw", MX, EM, XX }, + { "punpcklwd", MX, EM, XX }, + { "punpckldq", MX, EM, XX }, + { "packsswb", MX, EM, XX }, + { "pcmpgtb", MX, EM, XX }, + { "pcmpgtw", MX, EM, XX }, + { "pcmpgtd", MX, EM, XX }, + { "packuswb", MX, EM, XX }, + /* 68 */ + { "punpckhbw", MX, EM, XX }, + { "punpckhwd", MX, EM, XX }, + { "punpckhdq", MX, EM, XX }, + { "packssdw", MX, EM, XX }, + { PREGRP26 }, + { PREGRP24 }, + { "movd", MX, Ed, XX }, + { PREGRP19 }, + /* 70 */ + { PREGRP22 }, + { GRP10 }, + { GRP11 }, + { GRP12 }, + { "pcmpeqb", MX, EM, XX }, + { "pcmpeqw", MX, EM, XX }, + { "pcmpeqd", MX, EM, XX }, + { "emms", XX, XX, XX }, + /* 78 */ + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { PREGRP23 }, + { PREGRP20 }, + /* 80 */ + { "joH", Jv, XX, cond_jump_flag }, + { "jnoH", Jv, XX, cond_jump_flag }, + { "jbH", Jv, XX, cond_jump_flag }, + { "jaeH", Jv, XX, cond_jump_flag }, + { "jeH", Jv, XX, cond_jump_flag }, + { "jneH", Jv, XX, cond_jump_flag }, + { "jbeH", Jv, XX, cond_jump_flag }, + { "jaH", Jv, XX, cond_jump_flag }, + /* 88 */ + { "jsH", Jv, XX, cond_jump_flag }, + { "jnsH", Jv, XX, cond_jump_flag }, + { "jpH", Jv, XX, cond_jump_flag }, + { "jnpH", Jv, XX, cond_jump_flag }, + { "jlH", Jv, XX, cond_jump_flag }, + { "jgeH", Jv, XX, cond_jump_flag }, + { "jleH", Jv, XX, cond_jump_flag }, + { "jgH", Jv, XX, cond_jump_flag }, + /* 90 */ + { "seto", Eb, XX, XX }, + { "setno", Eb, XX, XX }, + { "setb", Eb, XX, XX }, + { "setae", Eb, XX, XX }, + { "sete", Eb, XX, XX }, + { "setne", Eb, XX, XX }, + { "setbe", Eb, XX, XX }, + { "seta", Eb, XX, XX }, + /* 98 */ + { "sets", Eb, XX, XX }, + { "setns", Eb, XX, XX }, + { "setp", Eb, XX, XX }, + { "setnp", Eb, XX, XX }, + { "setl", Eb, XX, XX }, + { "setge", Eb, XX, XX }, + { "setle", Eb, XX, XX }, + { "setg", Eb, XX, XX }, + /* a0 */ + { "pushT", fs, XX, XX }, + { "popT", fs, XX, XX }, + { "cpuid", XX, XX, XX }, + { "btS", Ev, Gv, XX }, + { "shldS", Ev, Gv, Ib }, + { "shldS", Ev, Gv, CL }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* a8 */ + { "pushT", gs, XX, XX }, + { "popT", gs, XX, XX }, + { "rsm", XX, XX, XX }, + { "btsS", Ev, Gv, XX }, + { "shrdS", Ev, Gv, Ib }, + { "shrdS", Ev, Gv, CL }, + { GRP13 }, + { "imulS", Gv, Ev, XX }, + /* b0 */ + { "cmpxchgB", Eb, Gb, XX }, + { "cmpxchgS", Ev, Gv, XX }, + { "lssS", Gv, Mp, XX }, + { "btrS", Ev, Gv, XX }, + { "lfsS", Gv, Mp, XX }, + { "lgsS", Gv, Mp, XX }, + { "movz{bR|x|bR|x}", Gv, Eb, XX }, + { "movz{wR|x|wR|x}", Gv, Ew, XX }, /* yes, there really is movzww ! */ + /* b8 */ + { "(bad)", XX, XX, XX }, + { "ud2b", XX, XX, XX }, + { GRP8 }, + { "btcS", Ev, Gv, XX }, + { "bsfS", Gv, Ev, XX }, + { "bsrS", Gv, Ev, XX }, + { "movs{bR|x|bR|x}", Gv, Eb, XX }, + { "movs{wR|x|wR|x}", Gv, Ew, XX }, /* yes, there really is movsww ! */ + /* c0 */ + { "xaddB", Eb, Gb, XX }, + { "xaddS", Ev, Gv, XX }, + { PREGRP1 }, + { "movntiS", Ev, Gv, XX }, + { "pinsrw", MX, Ed, Ib }, + { "pextrw", Gd, MS, Ib }, + { "shufpX", XM, EX, Ib }, + { GRP9 }, + /* c8 */ + { "bswap", RMeAX, XX, XX }, + { "bswap", RMeCX, XX, XX }, + { "bswap", RMeDX, XX, XX }, + { "bswap", RMeBX, XX, XX }, + { "bswap", RMeSP, XX, XX }, + { "bswap", RMeBP, XX, XX }, + { "bswap", RMeSI, XX, XX }, + { "bswap", RMeDI, XX, XX }, + /* d0 */ + { "(bad)", XX, XX, XX }, + { "psrlw", MX, EM, XX }, + { "psrld", MX, EM, XX }, + { "psrlq", MX, EM, XX }, + { "paddq", MX, EM, XX }, + { "pmullw", MX, EM, XX }, + { PREGRP21 }, + { "pmovmskb", Gd, MS, XX }, + /* d8 */ + { "psubusb", MX, EM, XX }, + { "psubusw", MX, EM, XX }, + { "pminub", MX, EM, XX }, + { "pand", MX, EM, XX }, + { "paddusb", MX, EM, XX }, + { "paddusw", MX, EM, XX }, + { "pmaxub", MX, EM, XX }, + { "pandn", MX, EM, XX }, + /* e0 */ + { "pavgb", MX, EM, XX }, + { "psraw", MX, EM, XX }, + { "psrad", MX, EM, XX }, + { "pavgw", MX, EM, XX }, + { "pmulhuw", MX, EM, XX }, + { "pmulhw", MX, EM, XX }, + { PREGRP15 }, + { PREGRP25 }, + /* e8 */ + { "psubsb", MX, EM, XX }, + { "psubsw", MX, EM, XX }, + { "pminsw", MX, EM, XX }, + { "por", MX, EM, XX }, + { "paddsb", MX, EM, XX }, + { "paddsw", MX, EM, XX }, + { "pmaxsw", MX, EM, XX }, + { "pxor", MX, EM, XX }, + /* f0 */ + { "(bad)", XX, XX, XX }, + { "psllw", MX, EM, XX }, + { "pslld", MX, EM, XX }, + { "psllq", MX, EM, XX }, + { "pmuludq", MX, EM, XX }, + { "pmaddwd", MX, EM, XX }, + { "psadbw", MX, EM, XX }, + { PREGRP18 }, + /* f8 */ + { "psubb", MX, EM, XX }, + { "psubw", MX, EM, XX }, + { "psubd", MX, EM, XX }, + { "psubq", MX, EM, XX }, + { "paddb", MX, EM, XX }, + { "paddw", MX, EM, XX }, + { "paddd", MX, EM, XX }, + { "(bad)", XX, XX, XX } +}; + +static const unsigned char onebyte_has_modrm[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 00 */ + /* 10 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 10 */ + /* 20 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 20 */ + /* 30 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 30 */ + /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40 */ + /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 50 */ + /* 60 */ 0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0, /* 60 */ + /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 70 */ + /* 80 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 80 */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 90 */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* a0 */ + /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* b0 */ + /* c0 */ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* c0 */ + /* d0 */ 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* d0 */ + /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* e0 */ + /* f0 */ 0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1 /* f0 */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +static const unsigned char twobyte_has_modrm[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,1, /* 0f */ + /* 10 */ 1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ + /* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4f */ + /* 50 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 5f */ + /* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6f */ + /* 70 */ 1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 9f */ + /* a0 */ 0,0,0,1,1,1,0,0,0,0,0,1,1,1,1,1, /* af */ + /* b0 */ 1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1, /* bf */ + /* c0 */ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* df */ + /* e0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* ef */ + /* f0 */ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0 /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +static const unsigned char twobyte_uses_SSE_prefix[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ + /* 10 */ 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ + /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */ + /* 50 */ 0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* 5f */ + /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1, /* 6f */ + /* 70 */ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */ + /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */ + /* c0 */ 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* df */ + /* e0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* ef */ + /* f0 */ 0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0 /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +static char obuf[100]; +static char *obufp; +static char scratchbuf[100]; +static unsigned char *start_codep; +static unsigned char *insn_codep; +static unsigned char *codep; +static disassemble_info *the_info; +static int mod; +static int rm; +static int reg; +static unsigned char need_modrm; + +/* If we are accessing mod/rm/reg without need_modrm set, then the + values are stale. Hitting this abort likely indicates that you + need to update onebyte_has_modrm or twobyte_has_modrm. */ +#define MODRM_CHECK if (!need_modrm) abort () + +static const char **names64; +static const char **names32; +static const char **names16; +static const char **names8; +static const char **names8rex; +static const char **names_seg; +static const char **index16; + +static const char *intel_names64[] = { + "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" +}; +static const char *intel_names32[] = { + "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", + "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" +}; +static const char *intel_names16[] = { + "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", + "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" +}; +static const char *intel_names8[] = { + "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", +}; +static const char *intel_names8rex[] = { + "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", + "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" +}; +static const char *intel_names_seg[] = { + "es", "cs", "ss", "ds", "fs", "gs", "?", "?", +}; +static const char *intel_index16[] = { + "bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx" +}; + +static const char *att_names64[] = { + "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" +}; +static const char *att_names32[] = { + "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", + "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d" +}; +static const char *att_names16[] = { + "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di", + "%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w" +}; +static const char *att_names8[] = { + "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh", +}; +static const char *att_names8rex[] = { + "%al", "%cl", "%dl", "%bl", "%spl", "%bpl", "%sil", "%dil", + "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b" +}; +static const char *att_names_seg[] = { + "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "%?", "%?", +}; +static const char *att_index16[] = { + "%bx,%si", "%bx,%di", "%bp,%si", "%bp,%di", "%si", "%di", "%bp", "%bx" +}; + +static const struct dis386 grps[][8] = { + /* GRP1b */ + { + { "addA", Eb, Ib, XX }, + { "orA", Eb, Ib, XX }, + { "adcA", Eb, Ib, XX }, + { "sbbA", Eb, Ib, XX }, + { "andA", Eb, Ib, XX }, + { "subA", Eb, Ib, XX }, + { "xorA", Eb, Ib, XX }, + { "cmpA", Eb, Ib, XX } + }, + /* GRP1S */ + { + { "addQ", Ev, Iv, XX }, + { "orQ", Ev, Iv, XX }, + { "adcQ", Ev, Iv, XX }, + { "sbbQ", Ev, Iv, XX }, + { "andQ", Ev, Iv, XX }, + { "subQ", Ev, Iv, XX }, + { "xorQ", Ev, Iv, XX }, + { "cmpQ", Ev, Iv, XX } + }, + /* GRP1Ss */ + { + { "addQ", Ev, sIb, XX }, + { "orQ", Ev, sIb, XX }, + { "adcQ", Ev, sIb, XX }, + { "sbbQ", Ev, sIb, XX }, + { "andQ", Ev, sIb, XX }, + { "subQ", Ev, sIb, XX }, + { "xorQ", Ev, sIb, XX }, + { "cmpQ", Ev, sIb, XX } + }, + /* GRP2b */ + { + { "rolA", Eb, Ib, XX }, + { "rorA", Eb, Ib, XX }, + { "rclA", Eb, Ib, XX }, + { "rcrA", Eb, Ib, XX }, + { "shlA", Eb, Ib, XX }, + { "shrA", Eb, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "sarA", Eb, Ib, XX }, + }, + /* GRP2S */ + { + { "rolQ", Ev, Ib, XX }, + { "rorQ", Ev, Ib, XX }, + { "rclQ", Ev, Ib, XX }, + { "rcrQ", Ev, Ib, XX }, + { "shlQ", Ev, Ib, XX }, + { "shrQ", Ev, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "sarQ", Ev, Ib, XX }, + }, + /* GRP2b_one */ + { + { "rolA", Eb, XX, XX }, + { "rorA", Eb, XX, XX }, + { "rclA", Eb, XX, XX }, + { "rcrA", Eb, XX, XX }, + { "shlA", Eb, XX, XX }, + { "shrA", Eb, XX, XX }, + { "(bad)", XX, XX, XX }, + { "sarA", Eb, XX, XX }, + }, + /* GRP2S_one */ + { + { "rolQ", Ev, XX, XX }, + { "rorQ", Ev, XX, XX }, + { "rclQ", Ev, XX, XX }, + { "rcrQ", Ev, XX, XX }, + { "shlQ", Ev, XX, XX }, + { "shrQ", Ev, XX, XX }, + { "(bad)", XX, XX, XX}, + { "sarQ", Ev, XX, XX }, + }, + /* GRP2b_cl */ + { + { "rolA", Eb, CL, XX }, + { "rorA", Eb, CL, XX }, + { "rclA", Eb, CL, XX }, + { "rcrA", Eb, CL, XX }, + { "shlA", Eb, CL, XX }, + { "shrA", Eb, CL, XX }, + { "(bad)", XX, XX, XX }, + { "sarA", Eb, CL, XX }, + }, + /* GRP2S_cl */ + { + { "rolQ", Ev, CL, XX }, + { "rorQ", Ev, CL, XX }, + { "rclQ", Ev, CL, XX }, + { "rcrQ", Ev, CL, XX }, + { "shlQ", Ev, CL, XX }, + { "shrQ", Ev, CL, XX }, + { "(bad)", XX, XX, XX }, + { "sarQ", Ev, CL, XX } + }, + /* GRP3b */ + { + { "testA", Eb, Ib, XX }, + { "(bad)", Eb, XX, XX }, + { "notA", Eb, XX, XX }, + { "negA", Eb, XX, XX }, + { "mulA", Eb, XX, XX }, /* Don't print the implicit %al register, */ + { "imulA", Eb, XX, XX }, /* to distinguish these opcodes from other */ + { "divA", Eb, XX, XX }, /* mul/imul opcodes. Do the same for div */ + { "idivA", Eb, XX, XX } /* and idiv for consistency. */ + }, + /* GRP3S */ + { + { "testQ", Ev, Iv, XX }, + { "(bad)", XX, XX, XX }, + { "notQ", Ev, XX, XX }, + { "negQ", Ev, XX, XX }, + { "mulQ", Ev, XX, XX }, /* Don't print the implicit register. */ + { "imulQ", Ev, XX, XX }, + { "divQ", Ev, XX, XX }, + { "idivQ", Ev, XX, XX }, + }, + /* GRP4 */ + { + { "incA", Eb, XX, XX }, + { "decA", Eb, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + }, + /* GRP5 */ + { + { "incQ", Ev, XX, XX }, + { "decQ", Ev, XX, XX }, + { "callT", indirEv, XX, XX }, + { "lcallT", indirEv, XX, XX }, + { "jmpT", indirEv, XX, XX }, + { "ljmpT", indirEv, XX, XX }, + { "pushU", Ev, XX, XX }, + { "(bad)", XX, XX, XX }, + }, + /* GRP6 */ + { + { "sldtQ", Ev, XX, XX }, + { "strQ", Ev, XX, XX }, + { "lldt", Ew, XX, XX }, + { "ltr", Ew, XX, XX }, + { "verr", Ew, XX, XX }, + { "verw", Ew, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX } + }, + /* GRP7 */ + { + { "sgdtQ", M, XX, XX }, + { "sidtQ", M, XX, XX }, + { "lgdtQ", M, XX, XX }, + { "lidtQ", M, XX, XX }, + { "smswQ", Ev, XX, XX }, + { "(bad)", XX, XX, XX }, + { "lmsw", Ew, XX, XX }, + { "invlpg", Ew, XX, XX }, + }, + /* GRP8 */ + { + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "btQ", Ev, Ib, XX }, + { "btsQ", Ev, Ib, XX }, + { "btrQ", Ev, Ib, XX }, + { "btcQ", Ev, Ib, XX }, + }, + /* GRP9 */ + { + { "(bad)", XX, XX, XX }, + { "cmpxchg8b", Ev, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + }, + /* GRP10 */ + { + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "psrlw", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "psraw", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "psllw", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + }, + /* GRP11 */ + { + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "psrld", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "psrad", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "pslld", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + }, + /* GRP12 */ + { + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "psrlq", MS, Ib, XX }, + { "psrldq", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "psllq", MS, Ib, XX }, + { "pslldq", MS, Ib, XX }, + }, + /* GRP13 */ + { + { "fxsave", Ev, XX, XX }, + { "fxrstor", Ev, XX, XX }, + { "ldmxcsr", Ev, XX, XX }, + { "stmxcsr", Ev, XX, XX }, + { "(bad)", XX, XX, XX }, + { "lfence", None, XX, XX }, + { "mfence", None, XX, XX }, + { "sfence", None, XX, XX }, + /* FIXME: the sfence with memory operand is clflush! */ + }, + /* GRP14 */ + { + { "prefetchnta", Ev, XX, XX }, + { "prefetcht0", Ev, XX, XX }, + { "prefetcht1", Ev, XX, XX }, + { "prefetcht2", Ev, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + }, + /* GRPAMD */ + { + { "prefetch", Eb, XX, XX }, + { "prefetchw", Eb, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + } +}; + +static const struct dis386 prefix_user_table[][4] = { + /* PREGRP0 */ + { + { "addps", XM, EX, XX }, + { "addss", XM, EX, XX }, + { "addpd", XM, EX, XX }, + { "addsd", XM, EX, XX }, + }, + /* PREGRP1 */ + { + { "", XM, EX, OPSIMD }, /* See OP_SIMD_SUFFIX. */ + { "", XM, EX, OPSIMD }, + { "", XM, EX, OPSIMD }, + { "", XM, EX, OPSIMD }, + }, + /* PREGRP2 */ + { + { "cvtpi2ps", XM, EM, XX }, + { "cvtsi2ssY", XM, Ev, XX }, + { "cvtpi2pd", XM, EM, XX }, + { "cvtsi2sdY", XM, Ev, XX }, + }, + /* PREGRP3 */ + { + { "cvtps2pi", MX, EX, XX }, + { "cvtss2siY", Gv, EX, XX }, + { "cvtpd2pi", MX, EX, XX }, + { "cvtsd2siY", Gv, EX, XX }, + }, + /* PREGRP4 */ + { + { "cvttps2pi", MX, EX, XX }, + { "cvttss2siY", Gv, EX, XX }, + { "cvttpd2pi", MX, EX, XX }, + { "cvttsd2siY", Gv, EX, XX }, + }, + /* PREGRP5 */ + { + { "divps", XM, EX, XX }, + { "divss", XM, EX, XX }, + { "divpd", XM, EX, XX }, + { "divsd", XM, EX, XX }, + }, + /* PREGRP6 */ + { + { "maxps", XM, EX, XX }, + { "maxss", XM, EX, XX }, + { "maxpd", XM, EX, XX }, + { "maxsd", XM, EX, XX }, + }, + /* PREGRP7 */ + { + { "minps", XM, EX, XX }, + { "minss", XM, EX, XX }, + { "minpd", XM, EX, XX }, + { "minsd", XM, EX, XX }, + }, + /* PREGRP8 */ + { + { "movups", XM, EX, XX }, + { "movss", XM, EX, XX }, + { "movupd", XM, EX, XX }, + { "movsd", XM, EX, XX }, + }, + /* PREGRP9 */ + { + { "movups", EX, XM, XX }, + { "movss", EX, XM, XX }, + { "movupd", EX, XM, XX }, + { "movsd", EX, XM, XX }, + }, + /* PREGRP10 */ + { + { "mulps", XM, EX, XX }, + { "mulss", XM, EX, XX }, + { "mulpd", XM, EX, XX }, + { "mulsd", XM, EX, XX }, + }, + /* PREGRP11 */ + { + { "rcpps", XM, EX, XX }, + { "rcpss", XM, EX, XX }, + { "(bad)", XM, EX, XX }, + { "(bad)", XM, EX, XX }, + }, + /* PREGRP12 */ + { + { "rsqrtps", XM, EX, XX }, + { "rsqrtss", XM, EX, XX }, + { "(bad)", XM, EX, XX }, + { "(bad)", XM, EX, XX }, + }, + /* PREGRP13 */ + { + { "sqrtps", XM, EX, XX }, + { "sqrtss", XM, EX, XX }, + { "sqrtpd", XM, EX, XX }, + { "sqrtsd", XM, EX, XX }, + }, + /* PREGRP14 */ + { + { "subps", XM, EX, XX }, + { "subss", XM, EX, XX }, + { "subpd", XM, EX, XX }, + { "subsd", XM, EX, XX }, + }, + /* PREGRP15 */ + { + { "(bad)", XM, EX, XX }, + { "cvtdq2pd", XM, EX, XX }, + { "cvttpd2dq", XM, EX, XX }, + { "cvtpd2dq", XM, EX, XX }, + }, + /* PREGRP16 */ + { + { "cvtdq2ps", XM, EX, XX }, + { "cvttps2dq",XM, EX, XX }, + { "cvtps2dq",XM, EX, XX }, + { "(bad)", XM, EX, XX }, + }, + /* PREGRP17 */ + { + { "cvtps2pd", XM, EX, XX }, + { "cvtss2sd", XM, EX, XX }, + { "cvtpd2ps", XM, EX, XX }, + { "cvtsd2ss", XM, EX, XX }, + }, + /* PREGRP18 */ + { + { "maskmovq", MX, MS, XX }, + { "(bad)", XM, EX, XX }, + { "maskmovdqu", XM, EX, XX }, + { "(bad)", XM, EX, XX }, + }, + /* PREGRP19 */ + { + { "movq", MX, EM, XX }, + { "movdqu", XM, EX, XX }, + { "movdqa", XM, EX, XX }, + { "(bad)", XM, EX, XX }, + }, + /* PREGRP20 */ + { + { "movq", EM, MX, XX }, + { "movdqu", EX, XM, XX }, + { "movdqa", EX, XM, XX }, + { "(bad)", EX, XM, XX }, + }, + /* PREGRP21 */ + { + { "(bad)", EX, XM, XX }, + { "movq2dq", XM, MS, XX }, + { "movq", EX, XM, XX }, + { "movdq2q", MX, XS, XX }, + }, + /* PREGRP22 */ + { + { "pshufw", MX, EM, Ib }, + { "pshufhw", XM, EX, Ib }, + { "pshufd", XM, EX, Ib }, + { "pshuflw", XM, EX, Ib }, + }, + /* PREGRP23 */ + { + { "movd", Ed, MX, XX }, + { "movq", XM, EX, XX }, + { "movd", Ed, XM, XX }, + { "(bad)", Ed, XM, XX }, + }, + /* PREGRP24 */ + { + { "(bad)", MX, EX, XX }, + { "(bad)", XM, EX, XX }, + { "punpckhqdq", XM, EX, XX }, + { "(bad)", XM, EX, XX }, + }, + /* PREGRP25 */ + { + { "movntq", Ev, MX, XX }, + { "(bad)", Ev, XM, XX }, + { "movntdq", Ev, XM, XX }, + { "(bad)", Ev, XM, XX }, + }, + /* PREGRP26 */ + { + { "(bad)", MX, EX, XX }, + { "(bad)", XM, EX, XX }, + { "punpcklqdq", XM, EX, XX }, + { "(bad)", XM, EX, XX }, + }, +}; + +static const struct dis386 x86_64_table[][2] = { + { + { "arpl", Ew, Gw, XX }, + { "movs{||lq|xd}", Gv, Ed, XX }, + }, +}; + +#define INTERNAL_DISASSEMBLER_ERROR _("") + +static void +ckprefix () +{ + int newrex; + rex = 0; + prefixes = 0; + used_prefixes = 0; + rex_used = 0; + while (1) + { + FETCH_DATA (the_info, codep + 1); + newrex = 0; + switch (*codep) + { + /* REX prefixes family. */ + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + if (mode_64bit) + newrex = *codep; + else + return; + break; + case 0xf3: + prefixes |= PREFIX_REPZ; + break; + case 0xf2: + prefixes |= PREFIX_REPNZ; + break; + case 0xf0: + prefixes |= PREFIX_LOCK; + break; + case 0x2e: + prefixes |= PREFIX_CS; + break; + case 0x36: + prefixes |= PREFIX_SS; + break; + case 0x3e: + prefixes |= PREFIX_DS; + break; + case 0x26: + prefixes |= PREFIX_ES; + break; + case 0x64: + prefixes |= PREFIX_FS; + break; + case 0x65: + prefixes |= PREFIX_GS; + break; + case 0x66: + prefixes |= PREFIX_DATA; + break; + case 0x67: + prefixes |= PREFIX_ADDR; + break; + case FWAIT_OPCODE: + /* fwait is really an instruction. If there are prefixes + before the fwait, they belong to the fwait, *not* to the + following instruction. */ + if (prefixes) + { + prefixes |= PREFIX_FWAIT; + codep++; + return; + } + prefixes = PREFIX_FWAIT; + break; + default: + return; + } + /* Rex is ignored when followed by another prefix. */ + if (rex) + { + oappend (prefix_name (rex, 0)); + oappend (" "); + } + rex = newrex; + codep++; + } +} + +/* Return the name of the prefix byte PREF, or NULL if PREF is not a + prefix byte. */ + +static const char * +prefix_name (pref, sizeflag) + int pref; + int sizeflag; +{ + switch (pref) + { + /* REX prefixes family. */ + case 0x40: + return "rex"; + case 0x41: + return "rexZ"; + case 0x42: + return "rexY"; + case 0x43: + return "rexYZ"; + case 0x44: + return "rexX"; + case 0x45: + return "rexXZ"; + case 0x46: + return "rexXY"; + case 0x47: + return "rexXYZ"; + case 0x48: + return "rex64"; + case 0x49: + return "rex64Z"; + case 0x4a: + return "rex64Y"; + case 0x4b: + return "rex64YZ"; + case 0x4c: + return "rex64X"; + case 0x4d: + return "rex64XZ"; + case 0x4e: + return "rex64XY"; + case 0x4f: + return "rex64XYZ"; + case 0xf3: + return "repz"; + case 0xf2: + return "repnz"; + case 0xf0: + return "lock"; + case 0x2e: + return "cs"; + case 0x36: + return "ss"; + case 0x3e: + return "ds"; + case 0x26: + return "es"; + case 0x64: + return "fs"; + case 0x65: + return "gs"; + case 0x66: + return (sizeflag & DFLAG) ? "data16" : "data32"; + case 0x67: + if (mode_64bit) + return (sizeflag & AFLAG) ? "addr32" : "addr64"; + else + return ((sizeflag & AFLAG) && !mode_64bit) ? "addr16" : "addr32"; + case FWAIT_OPCODE: + return "fwait"; + default: + return NULL; + } +} + +static char op1out[100], op2out[100], op3out[100]; +static int op_ad, op_index[3]; +static bfd_vma op_address[3]; +static bfd_vma op_riprel[3]; +static bfd_vma start_pc; + +/* + * On the 386's of 1988, the maximum length of an instruction is 15 bytes. + * (see topic "Redundant prefixes" in the "Differences from 8086" + * section of the "Virtual 8086 Mode" chapter.) + * 'pc' should be the address of this instruction, it will + * be used to print the target address if this is a relative jump or call + * The function returns the length of this instruction in bytes. + */ + +static int8_t intel_syntax; +static char open_char; +static char close_char; +static char separator_char; +static char scale_char; + +/* Here for backwards compatibility. When gdb stops using + print_insn_i386_att and print_insn_i386_intel these functions can + disappear, and print_insn_i386 be merged into print_insn. */ +int +print_insn_i386_att (pc, info) + bfd_vma pc; + disassemble_info *info; +{ + intel_syntax = 0; + + return print_insn (pc, info); +} + +int +print_insn_i386_intel (pc, info) + bfd_vma pc; + disassemble_info *info; +{ + intel_syntax = 1; + + return print_insn (pc, info); +} + +int +print_insn_i386 (pc, info) + bfd_vma pc; + disassemble_info *info; +{ + intel_syntax = -1; + + return print_insn (pc, info); +} + +static int +print_insn (pc, info) + bfd_vma pc; + disassemble_info *info; +{ + const struct dis386 *dp; + int i; + int two_source_ops; + char *first, *second, *third; + int needcomma; + unsigned char uses_SSE_prefix; + int sizeflag; + const char *p; + struct dis_private priv; + + mode_64bit = (info->mach == bfd_mach_x86_64_intel_syntax + || info->mach == bfd_mach_x86_64); + + if (intel_syntax == -1) + intel_syntax = (info->mach == bfd_mach_i386_i386_intel_syntax + || info->mach == bfd_mach_x86_64_intel_syntax); + + if (info->mach == bfd_mach_i386_i386 + || info->mach == bfd_mach_x86_64 + || info->mach == bfd_mach_i386_i386_intel_syntax + || info->mach == bfd_mach_x86_64_intel_syntax) + priv.orig_sizeflag = AFLAG | DFLAG; + else if (info->mach == bfd_mach_i386_i8086) + priv.orig_sizeflag = 0; + else + abort (); + + for (p = info->disassembler_options; p != NULL; ) + { + if (strncmp (p, "x86-64", 6) == 0) + { + mode_64bit = 1; + priv.orig_sizeflag = AFLAG | DFLAG; + } + else if (strncmp (p, "i386", 4) == 0) + { + mode_64bit = 0; + priv.orig_sizeflag = AFLAG | DFLAG; + } + else if (strncmp (p, "i8086", 5) == 0) + { + mode_64bit = 0; + priv.orig_sizeflag = 0; + } + else if (strncmp (p, "intel", 5) == 0) + { + intel_syntax = 1; + } + else if (strncmp (p, "att", 3) == 0) + { + intel_syntax = 0; + } + else if (strncmp (p, "addr", 4) == 0) + { + if (p[4] == '1' && p[5] == '6') + priv.orig_sizeflag &= ~AFLAG; + else if (p[4] == '3' && p[5] == '2') + priv.orig_sizeflag |= AFLAG; + } + else if (strncmp (p, "data", 4) == 0) + { + if (p[4] == '1' && p[5] == '6') + priv.orig_sizeflag &= ~DFLAG; + else if (p[4] == '3' && p[5] == '2') + priv.orig_sizeflag |= DFLAG; + } + else if (strncmp (p, "suffix", 6) == 0) + priv.orig_sizeflag |= SUFFIX_ALWAYS; + + p = strchr (p, ','); + if (p != NULL) + p++; + } + + if (intel_syntax) + { + names64 = intel_names64; + names32 = intel_names32; + names16 = intel_names16; + names8 = intel_names8; + names8rex = intel_names8rex; + names_seg = intel_names_seg; + index16 = intel_index16; + open_char = '['; + close_char = ']'; + separator_char = '+'; + scale_char = '*'; + } + else + { + names64 = att_names64; + names32 = att_names32; + names16 = att_names16; + names8 = att_names8; + names8rex = att_names8rex; + names_seg = att_names_seg; + index16 = att_index16; + open_char = '('; + close_char = ')'; + separator_char = ','; + scale_char = ','; + } + + /* The output looks better if we put 7 bytes on a line, since that + puts most long word instructions on a single line. */ + info->bytes_per_line = 7; + + info->private_data = (PTR) &priv; + priv.max_fetched = priv.the_buffer; + priv.insn_start = pc; + + obuf[0] = 0; + op1out[0] = 0; + op2out[0] = 0; + op3out[0] = 0; + + op_index[0] = op_index[1] = op_index[2] = -1; + + the_info = info; + start_pc = pc; + start_codep = priv.the_buffer; + codep = priv.the_buffer; + + if (setjmp (priv.bailout) != 0) + { + const char *name; + + /* Getting here means we tried for data but didn't get it. That + means we have an incomplete instruction of some sort. Just + print the first byte as a prefix or a .byte pseudo-op. */ + if (codep > priv.the_buffer) + { + name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); + if (name != NULL) + (*info->fprintf_func) (info->stream, "%s", name); + else + { + /* Just print the first byte as a .byte instruction. */ + (*info->fprintf_func) (info->stream, ".byte 0x%x", + (unsigned int) priv.the_buffer[0]); + } + + return 1; + } + + return -1; + } + + obufp = obuf; + ckprefix (); + + insn_codep = codep; + sizeflag = priv.orig_sizeflag; + + FETCH_DATA (info, codep + 1); + two_source_ops = (*codep == 0x62) || (*codep == 0xc8); + + if ((prefixes & PREFIX_FWAIT) + && ((*codep < 0xd8) || (*codep > 0xdf))) + { + const char *name; + + /* fwait not followed by floating point instruction. Print the + first prefix, which is probably fwait itself. */ + name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); + if (name == NULL) + name = INTERNAL_DISASSEMBLER_ERROR; + (*info->fprintf_func) (info->stream, "%s", name); + return 1; + } + + if (*codep == 0x0f) + { + FETCH_DATA (info, codep + 2); + dp = &dis386_twobyte[*++codep]; + need_modrm = twobyte_has_modrm[*codep]; + uses_SSE_prefix = twobyte_uses_SSE_prefix[*codep]; + } + else + { + dp = &dis386[*codep]; + need_modrm = onebyte_has_modrm[*codep]; + uses_SSE_prefix = 0; + } + codep++; + + if (!uses_SSE_prefix && (prefixes & PREFIX_REPZ)) + { + oappend ("repz "); + used_prefixes |= PREFIX_REPZ; + } + if (!uses_SSE_prefix && (prefixes & PREFIX_REPNZ)) + { + oappend ("repnz "); + used_prefixes |= PREFIX_REPNZ; + } + if (prefixes & PREFIX_LOCK) + { + oappend ("lock "); + used_prefixes |= PREFIX_LOCK; + } + + if (prefixes & PREFIX_ADDR) + { + sizeflag ^= AFLAG; + if (dp->bytemode3 != loop_jcxz_mode || intel_syntax) + { + if ((sizeflag & AFLAG) || mode_64bit) + oappend ("addr32 "); + else + oappend ("addr16 "); + used_prefixes |= PREFIX_ADDR; + } + } + + if (!uses_SSE_prefix && (prefixes & PREFIX_DATA)) + { + sizeflag ^= DFLAG; + if (dp->bytemode3 == cond_jump_mode + && dp->bytemode1 == v_mode + && !intel_syntax) + { + if (sizeflag & DFLAG) + oappend ("data32 "); + else + oappend ("data16 "); + used_prefixes |= PREFIX_DATA; + } + } + + if (need_modrm) + { + FETCH_DATA (info, codep + 1); + mod = (*codep >> 6) & 3; + reg = (*codep >> 3) & 7; + rm = *codep & 7; + } + + if (dp->name == NULL && dp->bytemode1 == FLOATCODE) + { + dofloat (sizeflag); + } + else + { + int index; + if (dp->name == NULL) + { + switch (dp->bytemode1) + { + case USE_GROUPS: + dp = &grps[dp->bytemode2][reg]; + break; + + case USE_PREFIX_USER_TABLE: + index = 0; + used_prefixes |= (prefixes & PREFIX_REPZ); + if (prefixes & PREFIX_REPZ) + index = 1; + else + { + used_prefixes |= (prefixes & PREFIX_DATA); + if (prefixes & PREFIX_DATA) + index = 2; + else + { + used_prefixes |= (prefixes & PREFIX_REPNZ); + if (prefixes & PREFIX_REPNZ) + index = 3; + } + } + dp = &prefix_user_table[dp->bytemode2][index]; + break; + + case X86_64_SPECIAL: + dp = &x86_64_table[dp->bytemode2][mode_64bit]; + break; + + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + break; + } + } + + if (putop (dp->name, sizeflag) == 0) + { + obufp = op1out; + op_ad = 2; + if (dp->op1) + (*dp->op1) (dp->bytemode1, sizeflag); + + obufp = op2out; + op_ad = 1; + if (dp->op2) + (*dp->op2) (dp->bytemode2, sizeflag); + + obufp = op3out; + op_ad = 0; + if (dp->op3) + (*dp->op3) (dp->bytemode3, sizeflag); + } + } + + /* See if any prefixes were not used. If so, print the first one + separately. If we don't do this, we'll wind up printing an + instruction stream which does not precisely correspond to the + bytes we are disassembling. */ + if ((prefixes & ~used_prefixes) != 0) + { + const char *name; + + name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag); + if (name == NULL) + name = INTERNAL_DISASSEMBLER_ERROR; + (*info->fprintf_func) (info->stream, "%s", name); + return 1; + } + if (rex & ~rex_used) + { + const char *name; + name = prefix_name (rex | 0x40, priv.orig_sizeflag); + if (name == NULL) + name = INTERNAL_DISASSEMBLER_ERROR; + (*info->fprintf_func) (info->stream, "%s ", name); + } + + obufp = obuf + strlen (obuf); + for (i = strlen (obuf); i < 6; i++) + oappend (" "); + oappend (" "); + (*info->fprintf_func) (info->stream, "%s", obuf); + + /* The enter and bound instructions are printed with operands in the same + order as the intel book; everything else is printed in reverse order. */ + if (intel_syntax || two_source_ops) + { + first = op1out; + second = op2out; + third = op3out; + op_ad = op_index[0]; + op_index[0] = op_index[2]; + op_index[2] = op_ad; + } + else + { + first = op3out; + second = op2out; + third = op1out; + } + needcomma = 0; + if (*first) + { + if (op_index[0] != -1 && !op_riprel[0]) + (*info->print_address_func) ((bfd_vma) op_address[op_index[0]], info); + else + (*info->fprintf_func) (info->stream, "%s", first); + needcomma = 1; + } + if (*second) + { + if (needcomma) + (*info->fprintf_func) (info->stream, ","); + if (op_index[1] != -1 && !op_riprel[1]) + (*info->print_address_func) ((bfd_vma) op_address[op_index[1]], info); + else + (*info->fprintf_func) (info->stream, "%s", second); + needcomma = 1; + } + if (*third) + { + if (needcomma) + (*info->fprintf_func) (info->stream, ","); + if (op_index[2] != -1 && !op_riprel[2]) + (*info->print_address_func) ((bfd_vma) op_address[op_index[2]], info); + else + (*info->fprintf_func) (info->stream, "%s", third); + } + for (i = 0; i < 3; i++) + if (op_index[i] != -1 && op_riprel[i]) + { + (*info->fprintf_func) (info->stream, " # "); + (*info->print_address_func) ((bfd_vma) (start_pc + codep - start_codep + + op_address[op_index[i]]), info); + } + return codep - priv.the_buffer; +} + +static const char *float_mem[] = { + /* d8 */ + "fadd{s||s|}", + "fmul{s||s|}", + "fcom{s||s|}", + "fcomp{s||s|}", + "fsub{s||s|}", + "fsubr{s||s|}", + "fdiv{s||s|}", + "fdivr{s||s|}", + /* d9 */ + "fld{s||s|}", + "(bad)", + "fst{s||s|}", + "fstp{s||s|}", + "fldenv", + "fldcw", + "fNstenv", + "fNstcw", + /* da */ + "fiadd{l||l|}", + "fimul{l||l|}", + "ficom{l||l|}", + "ficomp{l||l|}", + "fisub{l||l|}", + "fisubr{l||l|}", + "fidiv{l||l|}", + "fidivr{l||l|}", + /* db */ + "fild{l||l|}", + "(bad)", + "fist{l||l|}", + "fistp{l||l|}", + "(bad)", + "fld{t||t|}", + "(bad)", + "fstp{t||t|}", + /* dc */ + "fadd{l||l|}", + "fmul{l||l|}", + "fcom{l||l|}", + "fcomp{l||l|}", + "fsub{l||l|}", + "fsubr{l||l|}", + "fdiv{l||l|}", + "fdivr{l||l|}", + /* dd */ + "fld{l||l|}", + "(bad)", + "fst{l||l|}", + "fstp{l||l|}", + "frstor", + "(bad)", + "fNsave", + "fNstsw", + /* de */ + "fiadd", + "fimul", + "ficom", + "ficomp", + "fisub", + "fisubr", + "fidiv", + "fidivr", + /* df */ + "fild", + "(bad)", + "fist", + "fistp", + "fbld", + "fild{ll||ll|}", + "fbstp", + "fistpll", +}; + +#define ST OP_ST, 0 +#define STi OP_STi, 0 + +#define FGRPd9_2 NULL, NULL, 0, NULL, 0, NULL, 0 +#define FGRPd9_4 NULL, NULL, 1, NULL, 0, NULL, 0 +#define FGRPd9_5 NULL, NULL, 2, NULL, 0, NULL, 0 +#define FGRPd9_6 NULL, NULL, 3, NULL, 0, NULL, 0 +#define FGRPd9_7 NULL, NULL, 4, NULL, 0, NULL, 0 +#define FGRPda_5 NULL, NULL, 5, NULL, 0, NULL, 0 +#define FGRPdb_4 NULL, NULL, 6, NULL, 0, NULL, 0 +#define FGRPde_3 NULL, NULL, 7, NULL, 0, NULL, 0 +#define FGRPdf_4 NULL, NULL, 8, NULL, 0, NULL, 0 + +static const struct dis386 float_reg[][8] = { + /* d8 */ + { + { "fadd", ST, STi, XX }, + { "fmul", ST, STi, XX }, + { "fcom", STi, XX, XX }, + { "fcomp", STi, XX, XX }, + { "fsub", ST, STi, XX }, + { "fsubr", ST, STi, XX }, + { "fdiv", ST, STi, XX }, + { "fdivr", ST, STi, XX }, + }, + /* d9 */ + { + { "fld", STi, XX, XX }, + { "fxch", STi, XX, XX }, + { FGRPd9_2 }, + { "(bad)", XX, XX, XX }, + { FGRPd9_4 }, + { FGRPd9_5 }, + { FGRPd9_6 }, + { FGRPd9_7 }, + }, + /* da */ + { + { "fcmovb", ST, STi, XX }, + { "fcmove", ST, STi, XX }, + { "fcmovbe",ST, STi, XX }, + { "fcmovu", ST, STi, XX }, + { "(bad)", XX, XX, XX }, + { FGRPda_5 }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + }, + /* db */ + { + { "fcmovnb",ST, STi, XX }, + { "fcmovne",ST, STi, XX }, + { "fcmovnbe",ST, STi, XX }, + { "fcmovnu",ST, STi, XX }, + { FGRPdb_4 }, + { "fucomi", ST, STi, XX }, + { "fcomi", ST, STi, XX }, + { "(bad)", XX, XX, XX }, + }, + /* dc */ + { + { "fadd", STi, ST, XX }, + { "fmul", STi, ST, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, +#if UNIXWARE_COMPAT + { "fsub", STi, ST, XX }, + { "fsubr", STi, ST, XX }, + { "fdiv", STi, ST, XX }, + { "fdivr", STi, ST, XX }, +#else + { "fsubr", STi, ST, XX }, + { "fsub", STi, ST, XX }, + { "fdivr", STi, ST, XX }, + { "fdiv", STi, ST, XX }, +#endif + }, + /* dd */ + { + { "ffree", STi, XX, XX }, + { "(bad)", XX, XX, XX }, + { "fst", STi, XX, XX }, + { "fstp", STi, XX, XX }, + { "fucom", STi, XX, XX }, + { "fucomp", STi, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + }, + /* de */ + { + { "faddp", STi, ST, XX }, + { "fmulp", STi, ST, XX }, + { "(bad)", XX, XX, XX }, + { FGRPde_3 }, +#if UNIXWARE_COMPAT + { "fsubp", STi, ST, XX }, + { "fsubrp", STi, ST, XX }, + { "fdivp", STi, ST, XX }, + { "fdivrp", STi, ST, XX }, +#else + { "fsubrp", STi, ST, XX }, + { "fsubp", STi, ST, XX }, + { "fdivrp", STi, ST, XX }, + { "fdivp", STi, ST, XX }, +#endif + }, + /* df */ + { + { "ffreep", STi, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { FGRPdf_4 }, + { "fucomip",ST, STi, XX }, + { "fcomip", ST, STi, XX }, + { "(bad)", XX, XX, XX }, + }, +}; + +static char *fgrps[][8] = { + /* d9_2 0 */ + { + "fnop","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, + + /* d9_4 1 */ + { + "fchs","fabs","(bad)","(bad)","ftst","fxam","(bad)","(bad)", + }, + + /* d9_5 2 */ + { + "fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","(bad)", + }, + + /* d9_6 3 */ + { + "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp", + }, + + /* d9_7 4 */ + { + "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos", + }, + + /* da_5 5 */ + { + "(bad)","fucompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, + + /* db_4 6 */ + { + "feni(287 only)","fdisi(287 only)","fNclex","fNinit", + "fNsetpm(287 only)","(bad)","(bad)","(bad)", + }, + + /* de_3 7 */ + { + "(bad)","fcompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, + + /* df_4 8 */ + { + "fNstsw","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, +}; + +static void +dofloat (sizeflag) + int sizeflag; +{ + const struct dis386 *dp; + unsigned char floatop; + + floatop = codep[-1]; + + if (mod != 3) + { + putop (float_mem[(floatop - 0xd8) * 8 + reg], sizeflag); + obufp = op1out; + if (floatop == 0xdb) + OP_E (x_mode, sizeflag); + else if (floatop == 0xdd) + OP_E (d_mode, sizeflag); + else + OP_E (v_mode, sizeflag); + return; + } + /* Skip mod/rm byte. */ + MODRM_CHECK; + codep++; + + dp = &float_reg[floatop - 0xd8][reg]; + if (dp->name == NULL) + { + putop (fgrps[dp->bytemode1][rm], sizeflag); + + /* Instruction fnstsw is only one with strange arg. */ + if (floatop == 0xdf && codep[-1] == 0xe0) + strcpy (op1out, names16[0]); + } + else + { + putop (dp->name, sizeflag); + + obufp = op1out; + if (dp->op1) + (*dp->op1) (dp->bytemode1, sizeflag); + obufp = op2out; + if (dp->op2) + (*dp->op2) (dp->bytemode2, sizeflag); + } +} + +static void +OP_ST (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + oappend ("%st"); +} + +static void +OP_STi (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + sprintf (scratchbuf, "%%st(%d)", rm); + oappend (scratchbuf + intel_syntax); +} + +/* Capital letters in template are macros. */ +static int +putop (template, sizeflag) + const char *template; + int sizeflag; +{ + const char *p; + int alt; + + for (p = template; *p; p++) + { + switch (*p) + { + default: + *obufp++ = *p; + break; + case '{': + alt = 0; + if (intel_syntax) + alt += 1; + if (mode_64bit) + alt += 2; + while (alt != 0) + { + while (*++p != '|') + { + if (*p == '}') + { + /* Alternative not valid. */ + strcpy (obuf, "(bad)"); + obufp = obuf + 5; + return 1; + } + else if (*p == '\0') + abort (); + } + alt--; + } + break; + case '|': + while (*++p != '}') + { + if (*p == '\0') + abort (); + } + break; + case '}': + break; + case 'A': + if (intel_syntax) + break; + if (mod != 3 || (sizeflag & SUFFIX_ALWAYS)) + *obufp++ = 'b'; + break; + case 'B': + if (intel_syntax) + break; + if (sizeflag & SUFFIX_ALWAYS) + *obufp++ = 'b'; + break; + case 'E': /* For jcxz/jecxz */ + if (mode_64bit) + { + if (sizeflag & AFLAG) + *obufp++ = 'r'; + else + *obufp++ = 'e'; + } + else + if (sizeflag & AFLAG) + *obufp++ = 'e'; + used_prefixes |= (prefixes & PREFIX_ADDR); + break; + case 'F': + if (intel_syntax) + break; + if ((prefixes & PREFIX_ADDR) || (sizeflag & SUFFIX_ALWAYS)) + { + if (sizeflag & AFLAG) + *obufp++ = mode_64bit ? 'q' : 'l'; + else + *obufp++ = mode_64bit ? 'l' : 'w'; + used_prefixes |= (prefixes & PREFIX_ADDR); + } + break; + case 'H': + if (intel_syntax) + break; + if ((prefixes & (PREFIX_CS | PREFIX_DS)) == PREFIX_CS + || (prefixes & (PREFIX_CS | PREFIX_DS)) == PREFIX_DS) + { + used_prefixes |= prefixes & (PREFIX_CS | PREFIX_DS); + *obufp++ = ','; + *obufp++ = 'p'; + if (prefixes & PREFIX_DS) + *obufp++ = 't'; + else + *obufp++ = 'n'; + } + break; + case 'L': + if (intel_syntax) + break; + if (sizeflag & SUFFIX_ALWAYS) + *obufp++ = 'l'; + break; + case 'N': + if ((prefixes & PREFIX_FWAIT) == 0) + *obufp++ = 'n'; + else + used_prefixes |= PREFIX_FWAIT; + break; + case 'O': + USED_REX (REX_MODE64); + if (rex & REX_MODE64) + *obufp++ = 'o'; + else + *obufp++ = 'd'; + break; + case 'T': + if (intel_syntax) + break; + if (mode_64bit) + { + *obufp++ = 'q'; + break; + } + /* Fall through. */ + case 'P': + if (intel_syntax) + break; + if ((prefixes & PREFIX_DATA) + || (rex & REX_MODE64) + || (sizeflag & SUFFIX_ALWAYS)) + { + USED_REX (REX_MODE64); + if (rex & REX_MODE64) + *obufp++ = 'q'; + else + { + if (sizeflag & DFLAG) + *obufp++ = 'l'; + else + *obufp++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); + } + } + break; + case 'U': + if (intel_syntax) + break; + if (mode_64bit) + { + *obufp++ = 'q'; + break; + } + /* Fall through. */ + case 'Q': + if (intel_syntax) + break; + USED_REX (REX_MODE64); + if (mod != 3 || (sizeflag & SUFFIX_ALWAYS)) + { + if (rex & REX_MODE64) + *obufp++ = 'q'; + else + { + if (sizeflag & DFLAG) + *obufp++ = 'l'; + else + *obufp++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); + } + } + break; + case 'R': + USED_REX (REX_MODE64); + if (intel_syntax) + { + if (rex & REX_MODE64) + { + *obufp++ = 'q'; + *obufp++ = 't'; + } + else if (sizeflag & DFLAG) + { + *obufp++ = 'd'; + *obufp++ = 'q'; + } + else + { + *obufp++ = 'w'; + *obufp++ = 'd'; + } + } + else + { + if (rex & REX_MODE64) + *obufp++ = 'q'; + else if (sizeflag & DFLAG) + *obufp++ = 'l'; + else + *obufp++ = 'w'; + } + if (!(rex & REX_MODE64)) + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case 'S': + if (intel_syntax) + break; + if (sizeflag & SUFFIX_ALWAYS) + { + if (rex & REX_MODE64) + *obufp++ = 'q'; + else + { + if (sizeflag & DFLAG) + *obufp++ = 'l'; + else + *obufp++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); + } + } + break; + case 'X': + if (prefixes & PREFIX_DATA) + *obufp++ = 'd'; + else + *obufp++ = 's'; + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case 'Y': + if (intel_syntax) + break; + if (rex & REX_MODE64) + { + USED_REX (REX_MODE64); + *obufp++ = 'q'; + } + break; + /* implicit operand size 'l' for i386 or 'q' for x86-64 */ + case 'W': + /* operand size flag for cwtl, cbtw */ + USED_REX (0); + if (rex) + *obufp++ = 'l'; + else if (sizeflag & DFLAG) + *obufp++ = 'w'; + else + *obufp++ = 'b'; + if (intel_syntax) + { + if (rex) + { + *obufp++ = 'q'; + *obufp++ = 'e'; + } + if (sizeflag & DFLAG) + { + *obufp++ = 'd'; + *obufp++ = 'e'; + } + else + { + *obufp++ = 'w'; + } + } + if (!rex) + used_prefixes |= (prefixes & PREFIX_DATA); + break; + } + } + *obufp = 0; + return 0; +} + +static void +oappend (s) + const char *s; +{ + strcpy (obufp, s); + obufp += strlen (s); +} + +static void +append_seg () +{ + if (prefixes & PREFIX_CS) + { + used_prefixes |= PREFIX_CS; + oappend ("%cs:" + intel_syntax); + } + if (prefixes & PREFIX_DS) + { + used_prefixes |= PREFIX_DS; + oappend ("%ds:" + intel_syntax); + } + if (prefixes & PREFIX_SS) + { + used_prefixes |= PREFIX_SS; + oappend ("%ss:" + intel_syntax); + } + if (prefixes & PREFIX_ES) + { + used_prefixes |= PREFIX_ES; + oappend ("%es:" + intel_syntax); + } + if (prefixes & PREFIX_FS) + { + used_prefixes |= PREFIX_FS; + oappend ("%fs:" + intel_syntax); + } + if (prefixes & PREFIX_GS) + { + used_prefixes |= PREFIX_GS; + oappend ("%gs:" + intel_syntax); + } +} + +static void +OP_indirE (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + if (!intel_syntax) + oappend ("*"); + OP_E (bytemode, sizeflag); +} + +static void +print_operand_value (buf, hex, disp) + char *buf; + int hex; + bfd_vma disp; +{ + if (mode_64bit) + { + if (hex) + { + char tmp[30]; + int i; + buf[0] = '0'; + buf[1] = 'x'; + sprintf_vma (tmp, disp); + for (i = 0; tmp[i] == '0' && tmp[i + 1]; i++); + strcpy (buf + 2, tmp + i); + } + else + { + bfd_signed_vma v = disp; + char tmp[30]; + int i; + if (v < 0) + { + *(buf++) = '-'; + v = -disp; + /* Check for possible overflow on 0x8000000000000000. */ + if (v < 0) + { + strcpy (buf, "9223372036854775808"); + return; + } + } + if (!v) + { + strcpy (buf, "0"); + return; + } + + i = 0; + tmp[29] = 0; + while (v) + { + tmp[28 - i] = (v % 10) + '0'; + v /= 10; + i++; + } + strcpy (buf, tmp + 29 - i); + } + } + else + { + if (hex) + sprintf (buf, "0x%x", (unsigned int) disp); + else + sprintf (buf, "%d", (int) disp); + } +} + +static void +OP_E (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + bfd_vma disp; + int add = 0; + int riprel = 0; + USED_REX (REX_EXTZ); + if (rex & REX_EXTZ) + add += 8; + + /* Skip mod/rm byte. */ + MODRM_CHECK; + codep++; + + if (mod == 3) + { + switch (bytemode) + { + case b_mode: + USED_REX (0); + if (rex) + oappend (names8rex[rm + add]); + else + oappend (names8[rm + add]); + break; + case w_mode: + oappend (names16[rm + add]); + break; + case d_mode: + oappend (names32[rm + add]); + break; + case q_mode: + oappend (names64[rm + add]); + break; + case m_mode: + if (mode_64bit) + oappend (names64[rm + add]); + else + oappend (names32[rm + add]); + break; + case v_mode: + USED_REX (REX_MODE64); + if (rex & REX_MODE64) + oappend (names64[rm + add]); + else if (sizeflag & DFLAG) + oappend (names32[rm + add]); + else + oappend (names16[rm + add]); + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case 0: + if (!(codep[-2] == 0xAE && codep[-1] == 0xF8 /* sfence */) + && !(codep[-2] == 0xAE && codep[-1] == 0xF0 /* mfence */) + && !(codep[-2] == 0xAE && codep[-1] == 0xe8 /* lfence */)) + BadOp (); /* bad sfence,lea,lds,les,lfs,lgs,lss modrm */ + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + break; + } + return; + } + + disp = 0; + append_seg (); + + if ((sizeflag & AFLAG) || mode_64bit) /* 32 bit address mode */ + { + int havesib; + int havebase; + int base; + int index = 0; + int scale = 0; + + havesib = 0; + havebase = 1; + base = rm; + + if (base == 4) + { + havesib = 1; + FETCH_DATA (the_info, codep + 1); + scale = (*codep >> 6) & 3; + index = (*codep >> 3) & 7; + base = *codep & 7; + USED_REX (REX_EXTY); + USED_REX (REX_EXTZ); + if (rex & REX_EXTY) + index += 8; + if (rex & REX_EXTZ) + base += 8; + codep++; + } + + switch (mod) + { + case 0: + if ((base & 7) == 5) + { + havebase = 0; + if (mode_64bit && !havesib && (sizeflag & AFLAG)) + riprel = 1; + disp = get32s (); + } + break; + case 1: + FETCH_DATA (the_info, codep + 1); + disp = *codep++; + if ((disp & 0x80) != 0) + disp -= 0x100; + break; + case 2: + disp = get32s (); + break; + } + + if (!intel_syntax) + if (mod != 0 || (base & 7) == 5) + { + print_operand_value (scratchbuf, !riprel, disp); + oappend (scratchbuf); + if (riprel) + { + set_op (disp, 1); + oappend ("(%rip)"); + } + } + + if (havebase || (havesib && (index != 4 || scale != 0))) + { + if (intel_syntax) + { + switch (bytemode) + { + case b_mode: + oappend ("BYTE PTR "); + break; + case w_mode: + oappend ("WORD PTR "); + break; + case v_mode: + oappend ("DWORD PTR "); + break; + case d_mode: + oappend ("QWORD PTR "); + break; + case m_mode: + if (mode_64bit) + oappend ("DWORD PTR "); + else + oappend ("QWORD PTR "); + break; + case x_mode: + oappend ("XWORD PTR "); + break; + default: + break; + } + } + *obufp++ = open_char; + if (intel_syntax && riprel) + oappend ("rip + "); + *obufp = '\0'; + USED_REX (REX_EXTZ); + if (!havesib && (rex & REX_EXTZ)) + base += 8; + if (havebase) + oappend (mode_64bit && (sizeflag & AFLAG) + ? names64[base] : names32[base]); + if (havesib) + { + if (index != 4) + { + if (intel_syntax) + { + if (havebase) + { + *obufp++ = separator_char; + *obufp = '\0'; + } + sprintf (scratchbuf, "%s", + mode_64bit && (sizeflag & AFLAG) + ? names64[index] : names32[index]); + } + else + sprintf (scratchbuf, ",%s", + mode_64bit && (sizeflag & AFLAG) + ? names64[index] : names32[index]); + oappend (scratchbuf); + } + if (!intel_syntax + || (intel_syntax + && bytemode != b_mode + && bytemode != w_mode + && bytemode != v_mode)) + { + *obufp++ = scale_char; + *obufp = '\0'; + sprintf (scratchbuf, "%d", 1 << scale); + oappend (scratchbuf); + } + } + if (intel_syntax) + if (mod != 0 || (base & 7) == 5) + { + /* Don't print zero displacements. */ + if (disp != 0) + { + if ((bfd_signed_vma) disp > 0) + { + *obufp++ = '+'; + *obufp = '\0'; + } + + print_operand_value (scratchbuf, 0, disp); + oappend (scratchbuf); + } + } + + *obufp++ = close_char; + *obufp = '\0'; + } + else if (intel_syntax) + { + if (mod != 0 || (base & 7) == 5) + { + if (prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS + | PREFIX_ES | PREFIX_FS | PREFIX_GS)) + ; + else + { + oappend (names_seg[ds_reg - es_reg]); + oappend (":"); + } + print_operand_value (scratchbuf, 1, disp); + oappend (scratchbuf); + } + } + } + else + { /* 16 bit address mode */ + switch (mod) + { + case 0: + if ((rm & 7) == 6) + { + disp = get16 (); + if ((disp & 0x8000) != 0) + disp -= 0x10000; + } + break; + case 1: + FETCH_DATA (the_info, codep + 1); + disp = *codep++; + if ((disp & 0x80) != 0) + disp -= 0x100; + break; + case 2: + disp = get16 (); + if ((disp & 0x8000) != 0) + disp -= 0x10000; + break; + } + + if (!intel_syntax) + if (mod != 0 || (rm & 7) == 6) + { + print_operand_value (scratchbuf, 0, disp); + oappend (scratchbuf); + } + + if (mod != 0 || (rm & 7) != 6) + { + *obufp++ = open_char; + *obufp = '\0'; + oappend (index16[rm + add]); + *obufp++ = close_char; + *obufp = '\0'; + } + } +} + +static void +OP_G (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + int add = 0; + USED_REX (REX_EXTX); + if (rex & REX_EXTX) + add += 8; + switch (bytemode) + { + case b_mode: + USED_REX (0); + if (rex) + oappend (names8rex[reg + add]); + else + oappend (names8[reg + add]); + break; + case w_mode: + oappend (names16[reg + add]); + break; + case d_mode: + oappend (names32[reg + add]); + break; + case q_mode: + oappend (names64[reg + add]); + break; + case v_mode: + USED_REX (REX_MODE64); + if (rex & REX_MODE64) + oappend (names64[reg + add]); + else if (sizeflag & DFLAG) + oappend (names32[reg + add]); + else + oappend (names16[reg + add]); + used_prefixes |= (prefixes & PREFIX_DATA); + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + break; + } +} + +static bfd_vma +get64 () +{ + bfd_vma x; +#ifdef BFD64 + unsigned int a; + unsigned int b; + + FETCH_DATA (the_info, codep + 8); + a = *codep++ & 0xff; + a |= (*codep++ & 0xff) << 8; + a |= (*codep++ & 0xff) << 16; + a |= (*codep++ & 0xff) << 24; + b = *codep++ & 0xff; + b |= (*codep++ & 0xff) << 8; + b |= (*codep++ & 0xff) << 16; + b |= (*codep++ & 0xff) << 24; + x = a + ((bfd_vma) b << 32); +#else + abort (); + x = 0; +#endif + return x; +} + +static bfd_signed_vma +get32 () +{ + bfd_signed_vma x = 0; + + FETCH_DATA (the_info, codep + 4); + x = *codep++ & (bfd_signed_vma) 0xff; + x |= (*codep++ & (bfd_signed_vma) 0xff) << 8; + x |= (*codep++ & (bfd_signed_vma) 0xff) << 16; + x |= (*codep++ & (bfd_signed_vma) 0xff) << 24; + return x; +} + +static bfd_signed_vma +get32s () +{ + bfd_signed_vma x = 0; + + FETCH_DATA (the_info, codep + 4); + x = *codep++ & (bfd_signed_vma) 0xff; + x |= (*codep++ & (bfd_signed_vma) 0xff) << 8; + x |= (*codep++ & (bfd_signed_vma) 0xff) << 16; + x |= (*codep++ & (bfd_signed_vma) 0xff) << 24; + + x = (x ^ ((bfd_signed_vma) 1 << 31)) - ((bfd_signed_vma) 1 << 31); + + return x; +} + +static int +get16 () +{ + int x = 0; + + FETCH_DATA (the_info, codep + 2); + x = *codep++ & 0xff; + x |= (*codep++ & 0xff) << 8; + return x; +} + +static void +set_op (op, riprel) + bfd_vma op; + int riprel; +{ + op_index[op_ad] = op_ad; + if (mode_64bit) + { + op_address[op_ad] = op; + op_riprel[op_ad] = riprel; + } + else + { + /* Mask to get a 32-bit address. */ + op_address[op_ad] = op & 0xffffffff; + op_riprel[op_ad] = riprel & 0xffffffff; + } +} + +static void +OP_REG (code, sizeflag) + int code; + int sizeflag; +{ + const char *s; + int add = 0; + USED_REX (REX_EXTZ); + if (rex & REX_EXTZ) + add = 8; + + switch (code) + { + case indir_dx_reg: + if (intel_syntax) + s = "[dx]"; + else + s = "(%dx)"; + break; + case ax_reg: case cx_reg: case dx_reg: case bx_reg: + case sp_reg: case bp_reg: case si_reg: case di_reg: + s = names16[code - ax_reg + add]; + break; + case es_reg: case ss_reg: case cs_reg: + case ds_reg: case fs_reg: case gs_reg: + s = names_seg[code - es_reg + add]; + break; + case al_reg: case ah_reg: case cl_reg: case ch_reg: + case dl_reg: case dh_reg: case bl_reg: case bh_reg: + USED_REX (0); + if (rex) + s = names8rex[code - al_reg + add]; + else + s = names8[code - al_reg]; + break; + case rAX_reg: case rCX_reg: case rDX_reg: case rBX_reg: + case rSP_reg: case rBP_reg: case rSI_reg: case rDI_reg: + if (mode_64bit) + { + s = names64[code - rAX_reg + add]; + break; + } + code += eAX_reg - rAX_reg; + /* Fall through. */ + case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg: + case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg: + USED_REX (REX_MODE64); + if (rex & REX_MODE64) + s = names64[code - eAX_reg + add]; + else if (sizeflag & DFLAG) + s = names32[code - eAX_reg + add]; + else + s = names16[code - eAX_reg + add]; + used_prefixes |= (prefixes & PREFIX_DATA); + break; + default: + s = INTERNAL_DISASSEMBLER_ERROR; + break; + } + oappend (s); +} + +static void +OP_IMREG (code, sizeflag) + int code; + int sizeflag; +{ + const char *s; + + switch (code) + { + case indir_dx_reg: + if (intel_syntax) + s = "[dx]"; + else + s = "(%dx)"; + break; + case ax_reg: case cx_reg: case dx_reg: case bx_reg: + case sp_reg: case bp_reg: case si_reg: case di_reg: + s = names16[code - ax_reg]; + break; + case es_reg: case ss_reg: case cs_reg: + case ds_reg: case fs_reg: case gs_reg: + s = names_seg[code - es_reg]; + break; + case al_reg: case ah_reg: case cl_reg: case ch_reg: + case dl_reg: case dh_reg: case bl_reg: case bh_reg: + USED_REX (0); + if (rex) + s = names8rex[code - al_reg]; + else + s = names8[code - al_reg]; + break; + case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg: + case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg: + USED_REX (REX_MODE64); + if (rex & REX_MODE64) + s = names64[code - eAX_reg]; + else if (sizeflag & DFLAG) + s = names32[code - eAX_reg]; + else + s = names16[code - eAX_reg]; + used_prefixes |= (prefixes & PREFIX_DATA); + break; + default: + s = INTERNAL_DISASSEMBLER_ERROR; + break; + } + oappend (s); +} + +static void +OP_I (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + bfd_signed_vma op; + bfd_signed_vma mask = -1; + + switch (bytemode) + { + case b_mode: + FETCH_DATA (the_info, codep + 1); + op = *codep++; + mask = 0xff; + break; + case q_mode: + if (mode_64bit) + { + op = get32s (); + break; + } + /* Fall through. */ + case v_mode: + USED_REX (REX_MODE64); + if (rex & REX_MODE64) + op = get32s (); + else if (sizeflag & DFLAG) + { + op = get32 (); + mask = 0xffffffff; + } + else + { + op = get16 (); + mask = 0xfffff; + } + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case w_mode: + mask = 0xfffff; + op = get16 (); + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + return; + } + + op &= mask; + scratchbuf[0] = '$'; + print_operand_value (scratchbuf + 1, 1, op); + oappend (scratchbuf + intel_syntax); + scratchbuf[0] = '\0'; +} + +static void +OP_I64 (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + bfd_signed_vma op; + bfd_signed_vma mask = -1; + + if (!mode_64bit) + { + OP_I (bytemode, sizeflag); + return; + } + + switch (bytemode) + { + case b_mode: + FETCH_DATA (the_info, codep + 1); + op = *codep++; + mask = 0xff; + break; + case v_mode: + USED_REX (REX_MODE64); + if (rex & REX_MODE64) + op = get64 (); + else if (sizeflag & DFLAG) + { + op = get32 (); + mask = 0xffffffff; + } + else + { + op = get16 (); + mask = 0xfffff; + } + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case w_mode: + mask = 0xfffff; + op = get16 (); + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + return; + } + + op &= mask; + scratchbuf[0] = '$'; + print_operand_value (scratchbuf + 1, 1, op); + oappend (scratchbuf + intel_syntax); + scratchbuf[0] = '\0'; +} + +static void +OP_sI (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + bfd_signed_vma op; + bfd_signed_vma mask = -1; + + switch (bytemode) + { + case b_mode: + FETCH_DATA (the_info, codep + 1); + op = *codep++; + if ((op & 0x80) != 0) + op -= 0x100; + mask = 0xffffffff; + break; + case v_mode: + USED_REX (REX_MODE64); + if (rex & REX_MODE64) + op = get32s (); + else if (sizeflag & DFLAG) + { + op = get32s (); + mask = 0xffffffff; + } + else + { + mask = 0xffffffff; + op = get16 (); + if ((op & 0x8000) != 0) + op -= 0x10000; + } + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case w_mode: + op = get16 (); + mask = 0xffffffff; + if ((op & 0x8000) != 0) + op -= 0x10000; + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + return; + } + + scratchbuf[0] = '$'; + print_operand_value (scratchbuf + 1, 1, op); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_J (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + bfd_vma disp; + bfd_vma mask = -1; + + switch (bytemode) + { + case b_mode: + FETCH_DATA (the_info, codep + 1); + disp = *codep++; + if ((disp & 0x80) != 0) + disp -= 0x100; + break; + case v_mode: + if (sizeflag & DFLAG) + disp = get32s (); + else + { + disp = get16 (); + /* For some reason, a data16 prefix on a jump instruction + means that the pc is masked to 16 bits after the + displacement is added! */ + mask = 0xffff; + } + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + return; + } + disp = (start_pc + codep - start_codep + disp) & mask; + set_op (disp, 0); + print_operand_value (scratchbuf, 1, disp); + oappend (scratchbuf); +} + +static void +OP_SEG (dummy, sizeflag) + int dummy; + int sizeflag; +{ + oappend (names_seg[reg]); +} + +static void +OP_DIR (dummy, sizeflag) + int dummy; + int sizeflag; +{ + int seg, offset; + + if (sizeflag & DFLAG) + { + offset = get32 (); + seg = get16 (); + } + else + { + offset = get16 (); + seg = get16 (); + } + used_prefixes |= (prefixes & PREFIX_DATA); + if (intel_syntax) + sprintf (scratchbuf, "0x%x,0x%x", seg, offset); + else + sprintf (scratchbuf, "$0x%x,$0x%x", seg, offset); + oappend (scratchbuf); +} + +static void +OP_OFF (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + bfd_vma off; + + append_seg (); + + if ((sizeflag & AFLAG) || mode_64bit) + off = get32 (); + else + off = get16 (); + + if (intel_syntax) + { + if (!(prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS + | PREFIX_ES | PREFIX_FS | PREFIX_GS))) + { + oappend (names_seg[ds_reg - es_reg]); + oappend (":"); + } + } + print_operand_value (scratchbuf, 1, off); + oappend (scratchbuf); +} + +static void +OP_OFF64 (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + bfd_vma off; + + if (!mode_64bit) + { + OP_OFF (bytemode, sizeflag); + return; + } + + append_seg (); + + off = get64 (); + + if (intel_syntax) + { + if (!(prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS + | PREFIX_ES | PREFIX_FS | PREFIX_GS))) + { + oappend (names_seg[ds_reg - es_reg]); + oappend (":"); + } + } + print_operand_value (scratchbuf, 1, off); + oappend (scratchbuf); +} + +static void +ptr_reg (code, sizeflag) + int code; + int sizeflag; +{ + const char *s; + if (intel_syntax) + oappend ("["); + else + oappend ("("); + + USED_REX (REX_MODE64); + if (rex & REX_MODE64) + { + if (!(sizeflag & AFLAG)) + s = names32[code - eAX_reg]; + else + s = names64[code - eAX_reg]; + } + else if (sizeflag & AFLAG) + s = names32[code - eAX_reg]; + else + s = names16[code - eAX_reg]; + oappend (s); + if (intel_syntax) + oappend ("]"); + else + oappend (")"); +} + +static void +OP_ESreg (code, sizeflag) + int code; + int sizeflag; +{ + oappend ("%es:" + intel_syntax); + ptr_reg (code, sizeflag); +} + +static void +OP_DSreg (code, sizeflag) + int code; + int sizeflag; +{ + if ((prefixes + & (PREFIX_CS + | PREFIX_DS + | PREFIX_SS + | PREFIX_ES + | PREFIX_FS + | PREFIX_GS)) == 0) + prefixes |= PREFIX_DS; + append_seg (); + ptr_reg (code, sizeflag); +} + +static void +OP_C (dummy, sizeflag) + int dummy; + int sizeflag; +{ + int add = 0; + USED_REX (REX_EXTX); + if (rex & REX_EXTX) + add = 8; + sprintf (scratchbuf, "%%cr%d", reg + add); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_D (dummy, sizeflag) + int dummy; + int sizeflag; +{ + int add = 0; + USED_REX (REX_EXTX); + if (rex & REX_EXTX) + add = 8; + if (intel_syntax) + sprintf (scratchbuf, "db%d", reg + add); + else + sprintf (scratchbuf, "%%db%d", reg + add); + oappend (scratchbuf); +} + +static void +OP_T (dummy, sizeflag) + int dummy; + int sizeflag; +{ + sprintf (scratchbuf, "%%tr%d", reg); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_Rd (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + if (mod == 3) + OP_E (bytemode, sizeflag); + else + BadOp (); +} + +static void +OP_MMX (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + int add = 0; + USED_REX (REX_EXTX); + if (rex & REX_EXTX) + add = 8; + used_prefixes |= (prefixes & PREFIX_DATA); + if (prefixes & PREFIX_DATA) + sprintf (scratchbuf, "%%xmm%d", reg + add); + else + sprintf (scratchbuf, "%%mm%d", reg + add); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_XMM (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + int add = 0; + USED_REX (REX_EXTX); + if (rex & REX_EXTX) + add = 8; + sprintf (scratchbuf, "%%xmm%d", reg + add); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_EM (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + int add = 0; + if (mod != 3) + { + OP_E (bytemode, sizeflag); + return; + } + USED_REX (REX_EXTZ); + if (rex & REX_EXTZ) + add = 8; + + /* Skip mod/rm byte. */ + MODRM_CHECK; + codep++; + used_prefixes |= (prefixes & PREFIX_DATA); + if (prefixes & PREFIX_DATA) + sprintf (scratchbuf, "%%xmm%d", rm + add); + else + sprintf (scratchbuf, "%%mm%d", rm + add); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_EX (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + int add = 0; + if (mod != 3) + { + OP_E (bytemode, sizeflag); + return; + } + USED_REX (REX_EXTZ); + if (rex & REX_EXTZ) + add = 8; + + /* Skip mod/rm byte. */ + MODRM_CHECK; + codep++; + sprintf (scratchbuf, "%%xmm%d", rm + add); + oappend (scratchbuf + intel_syntax); +} + +static void +OP_MS (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + if (mod == 3) + OP_EM (bytemode, sizeflag); + else + BadOp (); +} + +static void +OP_XS (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + if (mod == 3) + OP_EX (bytemode, sizeflag); + else + BadOp (); +} + +static const char *Suffix3DNow[] = { +/* 00 */ NULL, NULL, NULL, NULL, +/* 04 */ NULL, NULL, NULL, NULL, +/* 08 */ NULL, NULL, NULL, NULL, +/* 0C */ "pi2fw", "pi2fd", NULL, NULL, +/* 10 */ NULL, NULL, NULL, NULL, +/* 14 */ NULL, NULL, NULL, NULL, +/* 18 */ NULL, NULL, NULL, NULL, +/* 1C */ "pf2iw", "pf2id", NULL, NULL, +/* 20 */ NULL, NULL, NULL, NULL, +/* 24 */ NULL, NULL, NULL, NULL, +/* 28 */ NULL, NULL, NULL, NULL, +/* 2C */ NULL, NULL, NULL, NULL, +/* 30 */ NULL, NULL, NULL, NULL, +/* 34 */ NULL, NULL, NULL, NULL, +/* 38 */ NULL, NULL, NULL, NULL, +/* 3C */ NULL, NULL, NULL, NULL, +/* 40 */ NULL, NULL, NULL, NULL, +/* 44 */ NULL, NULL, NULL, NULL, +/* 48 */ NULL, NULL, NULL, NULL, +/* 4C */ NULL, NULL, NULL, NULL, +/* 50 */ NULL, NULL, NULL, NULL, +/* 54 */ NULL, NULL, NULL, NULL, +/* 58 */ NULL, NULL, NULL, NULL, +/* 5C */ NULL, NULL, NULL, NULL, +/* 60 */ NULL, NULL, NULL, NULL, +/* 64 */ NULL, NULL, NULL, NULL, +/* 68 */ NULL, NULL, NULL, NULL, +/* 6C */ NULL, NULL, NULL, NULL, +/* 70 */ NULL, NULL, NULL, NULL, +/* 74 */ NULL, NULL, NULL, NULL, +/* 78 */ NULL, NULL, NULL, NULL, +/* 7C */ NULL, NULL, NULL, NULL, +/* 80 */ NULL, NULL, NULL, NULL, +/* 84 */ NULL, NULL, NULL, NULL, +/* 88 */ NULL, NULL, "pfnacc", NULL, +/* 8C */ NULL, NULL, "pfpnacc", NULL, +/* 90 */ "pfcmpge", NULL, NULL, NULL, +/* 94 */ "pfmin", NULL, "pfrcp", "pfrsqrt", +/* 98 */ NULL, NULL, "pfsub", NULL, +/* 9C */ NULL, NULL, "pfadd", NULL, +/* A0 */ "pfcmpgt", NULL, NULL, NULL, +/* A4 */ "pfmax", NULL, "pfrcpit1", "pfrsqit1", +/* A8 */ NULL, NULL, "pfsubr", NULL, +/* AC */ NULL, NULL, "pfacc", NULL, +/* B0 */ "pfcmpeq", NULL, NULL, NULL, +/* B4 */ "pfmul", NULL, "pfrcpit2", "pfmulhrw", +/* B8 */ NULL, NULL, NULL, "pswapd", +/* BC */ NULL, NULL, NULL, "pavgusb", +/* C0 */ NULL, NULL, NULL, NULL, +/* C4 */ NULL, NULL, NULL, NULL, +/* C8 */ NULL, NULL, NULL, NULL, +/* CC */ NULL, NULL, NULL, NULL, +/* D0 */ NULL, NULL, NULL, NULL, +/* D4 */ NULL, NULL, NULL, NULL, +/* D8 */ NULL, NULL, NULL, NULL, +/* DC */ NULL, NULL, NULL, NULL, +/* E0 */ NULL, NULL, NULL, NULL, +/* E4 */ NULL, NULL, NULL, NULL, +/* E8 */ NULL, NULL, NULL, NULL, +/* EC */ NULL, NULL, NULL, NULL, +/* F0 */ NULL, NULL, NULL, NULL, +/* F4 */ NULL, NULL, NULL, NULL, +/* F8 */ NULL, NULL, NULL, NULL, +/* FC */ NULL, NULL, NULL, NULL, +}; + +static void +OP_3DNowSuffix (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + const char *mnemonic; + + FETCH_DATA (the_info, codep + 1); + /* AMD 3DNow! instructions are specified by an opcode suffix in the + place where an 8-bit immediate would normally go. ie. the last + byte of the instruction. */ + obufp = obuf + strlen (obuf); + mnemonic = Suffix3DNow[*codep++ & 0xff]; + if (mnemonic) + oappend (mnemonic); + else + { + /* Since a variable sized modrm/sib chunk is between the start + of the opcode (0x0f0f) and the opcode suffix, we need to do + all the modrm processing first, and don't know until now that + we have a bad opcode. This necessitates some cleaning up. */ + op1out[0] = '\0'; + op2out[0] = '\0'; + BadOp (); + } +} + +static const char *simd_cmp_op[] = { + "eq", + "lt", + "le", + "unord", + "neq", + "nlt", + "nle", + "ord" +}; + +static void +OP_SIMD_Suffix (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + unsigned int cmp_type; + + FETCH_DATA (the_info, codep + 1); + obufp = obuf + strlen (obuf); + cmp_type = *codep++ & 0xff; + if (cmp_type < 8) + { + char suffix1 = 'p', suffix2 = 's'; + used_prefixes |= (prefixes & PREFIX_REPZ); + if (prefixes & PREFIX_REPZ) + suffix1 = 's'; + else + { + used_prefixes |= (prefixes & PREFIX_DATA); + if (prefixes & PREFIX_DATA) + suffix2 = 'd'; + else + { + used_prefixes |= (prefixes & PREFIX_REPNZ); + if (prefixes & PREFIX_REPNZ) + suffix1 = 's', suffix2 = 'd'; + } + } + sprintf (scratchbuf, "cmp%s%c%c", + simd_cmp_op[cmp_type], suffix1, suffix2); + used_prefixes |= (prefixes & PREFIX_REPZ); + oappend (scratchbuf); + } + else + { + /* We have a bad extension byte. Clean up. */ + op1out[0] = '\0'; + op2out[0] = '\0'; + BadOp (); + } +} + +static void +SIMD_Fixup (extrachar, sizeflag) + int extrachar; + int sizeflag; +{ + /* Change movlps/movhps to movhlps/movlhps for 2 register operand + forms of these instructions. */ + if (mod == 3) + { + char *p = obuf + strlen (obuf); + *(p + 1) = '\0'; + *p = *(p - 1); + *(p - 1) = *(p - 2); + *(p - 2) = *(p - 3); + *(p - 3) = extrachar; + } +} + +static void +BadOp (void) +{ + /* Throw away prefixes and 1st. opcode byte. */ + codep = insn_codep + 1; + oappend ("(bad)"); +} diff --git a/tools/ioemu/i386-vl.ld b/tools/ioemu/i386-vl.ld new file mode 100644 index 0000000000..428fe83e12 --- /dev/null +++ b/tools/ioemu/i386-vl.ld @@ -0,0 +1,140 @@ +/* ld script to make i386 Linux kernel + * Written by Martin Mares ; + */ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib); +ENTRY(_start) +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0xa8000000 + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.text : + { *(.rel.text) *(.rel.gnu.linkonce.t*) } + .rela.text : + { *(.rela.text) *(.rela.gnu.linkonce.t*) } + .rel.data : + { *(.rel.data) *(.rel.gnu.linkonce.d*) } + .rela.data : + { *(.rela.data) *(.rela.gnu.linkonce.d*) } + .rel.rodata : + { *(.rel.rodata) *(.rel.gnu.linkonce.r*) } + .rela.rodata : + { *(.rela.rodata) *(.rela.gnu.linkonce.r*) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } =0x47ff041f + .text : + { + *(.text) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.gnu.linkonce.t*) + } =0x47ff041f + _etext = .; + PROVIDE (etext = .); + .fini : { *(.fini) } =0x47ff041f + .rodata : { *(.rodata) *(.gnu.linkonce.r*) } + .rodata1 : { *(.rodata1) } + .reginfo : { *(.reginfo) } + __preinit_array_start = .; + .preinit_array : { *(.preinit_array) } + __preinit_array_end = .; + __init_array_start = .; + .init_array : { *(.init_array) } + __init_array_end = .; + __fini_array_start = .; + .fini_array : { *(.fini_array) } + __fini_array_end = .; + + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN(0x100000) + (. & (0x100000 - 1)); + .data : + { + *(.data) + *(.gnu.linkonce.d*) + CONSTRUCTORS + } + .data1 : { *(.data1) } + .ctors : + { + *(.ctors) + } + .dtors : + { + *(.dtors) + } + .plt : { *(.plt) } + .got : { *(.got.plt) *(.got) } + .dynamic : { *(.dynamic) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : { *(.sdata) } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .sbss : { *(.sbss) *(.scommon) } + .bss : + { + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* These must appear regardless of . */ +} diff --git a/tools/ioemu/i386.ld b/tools/ioemu/i386.ld new file mode 100644 index 0000000000..d41c62695e --- /dev/null +++ b/tools/ioemu/i386.ld @@ -0,0 +1,140 @@ +/* ld script to make i386 Linux kernel + * Written by Martin Mares ; + */ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib); +ENTRY(_start) +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0x60000000 + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.text : + { *(.rel.text) *(.rel.gnu.linkonce.t*) } + .rela.text : + { *(.rela.text) *(.rela.gnu.linkonce.t*) } + .rel.data : + { *(.rel.data) *(.rel.gnu.linkonce.d*) } + .rela.data : + { *(.rela.data) *(.rela.gnu.linkonce.d*) } + .rel.rodata : + { *(.rel.rodata) *(.rel.gnu.linkonce.r*) } + .rela.rodata : + { *(.rela.rodata) *(.rela.gnu.linkonce.r*) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } =0x47ff041f + .text : + { + *(.text) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.gnu.linkonce.t*) + } =0x47ff041f + _etext = .; + PROVIDE (etext = .); + .fini : { *(.fini) } =0x47ff041f + . = ALIGN(32 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array : { *(.preinit_array) } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { *(.init_array) } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { *(.fini_array) } + PROVIDE (__fini_array_end = .); + .rodata : { *(.rodata) *(.gnu.linkonce.r*) } + .rodata1 : { *(.rodata1) } + .reginfo : { *(.reginfo) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN(0x100000) + (. & (0x100000 - 1)); + .data : + { + *(.data) + *(.gnu.linkonce.d*) + CONSTRUCTORS + } + .data1 : { *(.data1) } + .ctors : + { + *(.ctors) + } + .dtors : + { + *(.dtors) + } + .plt : { *(.plt) } + .got : { *(.got.plt) *(.got) } + .dynamic : { *(.dynamic) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : { *(.sdata) } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .sbss : { *(.sbss) *(.scommon) } + .bss : + { + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* These must appear regardless of . */ +} diff --git a/tools/ioemu/ia64.ld b/tools/ioemu/ia64.ld new file mode 100644 index 0000000000..8d2ede2d3d --- /dev/null +++ b/tools/ioemu/ia64.ld @@ -0,0 +1,211 @@ +/* Default linker script, for normal executables */ +OUTPUT_FORMAT("elf64-ia64-little", "elf64-ia64-little", + "elf64-ia64-little") +OUTPUT_ARCH(ia64) +ENTRY(_start) +SEARCH_DIR("/usr/ia64-linux/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib"); +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = 0x60000000); . = 0x60000000 + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) } + .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) } + .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) } + .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) } + .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) } + .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) } + .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) } + .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) } + .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.sdata : { *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) } + .rela.sdata : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) } + .rel.sbss : { *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) } + .rela.sbss : { *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) } + .rel.sdata2 : { *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) } + .rela.sdata2 : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) } + .rel.sbss2 : { *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) } + .rela.sbss2 : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) } + .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) } + .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .rela.IA_64.pltoff : { *(.rela.IA_64.pltoff) } + .init : + { + KEEP (*(.init)) + } =0x00300000010070000002000001000400 + .plt : { *(.plt) } + .text : + { + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + } =0x00300000010070000002000001000400 + .fini : + { + KEEP (*(.fini)) + } =0x00300000010070000002000001000400 + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) } + .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } + .opd : { *(.opd) } + .IA_64.unwind_info : { *(.IA_64.unwind_info* .gnu.linkonce.ia64unwi.*) } + .IA_64.unwind : { *(.IA_64.unwind* .gnu.linkonce.ia64unw.*) } + .eh_frame_hdr : { *(.eh_frame_hdr) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN(0x10000) + (. & (0x10000 - 1)); + /* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(64 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array : { *(.preinit_array) } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { *(.init_array) } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { *(.fini_array) } + PROVIDE (__fini_array_end = .); + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .eh_frame : { KEEP (*(.eh_frame)) } + .gcc_except_table : { *(.gcc_except_table) } + .dynamic : { *(.dynamic) } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin*.o(.ctors)) + /* We don't want to include the .ctor section from + from the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin*.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + /* Ensure __gp is outside the range of any normal data. We need to + do this to avoid the linker optimizing the code in op.o and getting + it out of sync with the relocs that we read when processing that + file. A better solution might be to ensure that the dynamically + generated code and static qemu code share a single gp-value. */ + __gp = . + 0x200000; + .got : { *(.got.plt) *(.got) } + .IA_64.pltoff : { *(.IA_64.pltoff) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : + { + *(.sdata .sdata.* .gnu.linkonce.s.*) + } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .sbss : + { + PROVIDE (__sbss_start = .); + PROVIDE (___sbss_start = .); + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + PROVIDE (__sbss_end = .); + PROVIDE (___sbss_end = .); + } + .bss : + { + . += 0x400000; /* ensure .bss stuff is out of reach of gp */ + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + . = ALIGN(64 / 8); + } + . = ALIGN(64 / 8); + _end = .; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /DISCARD/ : { *(.note.GNU-stack) } +} diff --git a/tools/ioemu/keymaps.c b/tools/ioemu/keymaps.c new file mode 100644 index 0000000000..bd893288aa --- /dev/null +++ b/tools/ioemu/keymaps.c @@ -0,0 +1,145 @@ +/* + * QEMU keysym to keycode conversion using rdesktop keymaps + * + * Copyright (c) 2004 Johannes Schindelin + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +static int get_keysym(const char *name) +{ + name2keysym_t *p; + for(p = name2keysym; p->name != NULL; p++) { + if (!strcmp(p->name, name)) + return p->keysym; + } + return 0; +} + +#define MAX_NORMAL_KEYCODE 512 +#define MAX_EXTRA_COUNT 256 +typedef struct { + uint16_t keysym2keycode[MAX_NORMAL_KEYCODE]; + struct { + int keysym; + uint16_t keycode; + } keysym2keycode_extra[MAX_EXTRA_COUNT]; + int extra_count; +} kbd_layout_t; + +static kbd_layout_t *parse_keyboard_layout(const char *language, + kbd_layout_t * k) +{ + FILE *f; + char file_name[1024]; + char line[1024]; + int len; + + snprintf(file_name, sizeof(file_name), + "%s/keymaps/%s", bios_dir, language); + + if (!k) + k = qemu_mallocz(sizeof(kbd_layout_t)); + if (!k) + return 0; + if (!(f = fopen(file_name, "r"))) { + fprintf(stderr, + "Could not read keymap file: '%s'\n", file_name); + return 0; + } + for(;;) { + if (fgets(line, 1024, f) == NULL) + break; + len = strlen(line); + if (len > 0 && line[len - 1] == '\n') + line[len - 1] = '\0'; + if (line[0] == '#') + continue; + if (!strncmp(line, "map ", 4)) + continue; + if (!strncmp(line, "include ", 8)) { + parse_keyboard_layout(line + 8, k); + } else { + char *end_of_keysym = line; + while (*end_of_keysym != 0 && *end_of_keysym != ' ') + end_of_keysym++; + if (*end_of_keysym) { + int keysym; + *end_of_keysym = 0; + keysym = get_keysym(line); + if (keysym == 0) { + // fprintf(stderr, "Warning: unknown keysym %s\n", line); + } else { + const char *rest = end_of_keysym + 1; + int keycode = strtol(rest, NULL, 0); + /* if(keycode&0x80) + keycode=(keycode<<8)^0x80e0; */ + if (keysym < MAX_NORMAL_KEYCODE) { + //fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode); + k->keysym2keycode[keysym] = keycode; + } else { + if (k->extra_count >= MAX_EXTRA_COUNT) { + fprintf(stderr, + "Warning: Could not assign keysym %s (0x%x) because of memory constraints.\n", + line, keysym); + } else { +#if 0 + fprintf(stderr, "Setting %d: %d,%d\n", + k->extra_count, keysym, keycode); +#endif + k->keysym2keycode_extra[k->extra_count]. + keysym = keysym; + k->keysym2keycode_extra[k->extra_count]. + keycode = keycode; + k->extra_count++; + } + } + } + } + } + } + fclose(f); + return k; +} + +static void *init_keyboard_layout(const char *language) +{ + return parse_keyboard_layout(language, 0); +} + +static int keysym2scancode(void *kbd_layout, int keysym) +{ + kbd_layout_t *k = kbd_layout; + if (keysym < MAX_NORMAL_KEYCODE) { + if (k->keysym2keycode[keysym] == 0) + fprintf(stderr, "Warning: no scancode found for keysym %d\n", + keysym); + return k->keysym2keycode[keysym]; + } else { + int i; +#ifdef XK_ISO_Left_Tab + if (keysym == XK_ISO_Left_Tab) + keysym = XK_Tab; +#endif + for (i = 0; i < k->extra_count; i++) + if (k->keysym2keycode_extra[i].keysym == keysym) + return k->keysym2keycode_extra[i].keycode; + } + return 0; +} diff --git a/tools/ioemu/keymaps/CVS/Entries b/tools/ioemu/keymaps/CVS/Entries new file mode 100644 index 0000000000..db41d231e0 --- /dev/null +++ b/tools/ioemu/keymaps/CVS/Entries @@ -0,0 +1,36 @@ +/ar/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/common/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/da/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/de/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/de-ch/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/en-gb/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/en-us/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/es/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/et/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/fi/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/fo/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/fr/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/fr-be/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/fr-ca/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/fr-ch/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/hr/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/hu/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/is/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/it/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/ja/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/lt/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/lv/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/mk/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/modifiers/1.2/Tue Mar 1 21:43:42 2005//Trelease_0_8_1 +/nl/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/nl-be/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/no/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/pl/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/pt/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/pt-br/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/ru/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/sl/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/sv/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/th/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +/tr/1.1/Sun Dec 12 16:56:30 2004//Trelease_0_8_1 +D diff --git a/tools/ioemu/keymaps/CVS/Repository b/tools/ioemu/keymaps/CVS/Repository new file mode 100644 index 0000000000..2351e25b08 --- /dev/null +++ b/tools/ioemu/keymaps/CVS/Repository @@ -0,0 +1 @@ +qemu/keymaps diff --git a/tools/ioemu/keymaps/CVS/Root b/tools/ioemu/keymaps/CVS/Root new file mode 100644 index 0000000000..e8a9815e6a --- /dev/null +++ b/tools/ioemu/keymaps/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.savannah.nongnu.org:/sources/qemu diff --git a/tools/ioemu/keymaps/CVS/Tag b/tools/ioemu/keymaps/CVS/Tag new file mode 100644 index 0000000000..31c251a4bf --- /dev/null +++ b/tools/ioemu/keymaps/CVS/Tag @@ -0,0 +1 @@ +Nrelease_0_8_1 diff --git a/tools/ioemu/keymaps/ar b/tools/ioemu/keymaps/ar new file mode 100644 index 0000000000..c430c03bb3 --- /dev/null +++ b/tools/ioemu/keymaps/ar @@ -0,0 +1,98 @@ +# generated from XKB map ar +include common +map 0x401 +exclam 0x02 shift +at 0x03 shift +numbersign 0x04 shift +dollar 0x05 shift +percent 0x06 shift +asciicircum 0x07 shift +ampersand 0x08 shift +asterisk 0x09 shift +parenleft 0x0a shift +parenright 0x0b shift +minus 0x0c +underscore 0x0c shift +equal 0x0d +plus 0x0d shift +Arabic_dad 0x10 altgr +Arabic_fatha 0x10 shift altgr +Arabic_sad 0x11 altgr +Arabic_fathatan 0x11 shift altgr +Arabic_theh 0x12 altgr +Arabic_damma 0x12 shift altgr +Arabic_qaf 0x13 altgr +Arabic_dammatan 0x13 shift altgr +Arabic_feh 0x14 altgr +UFEF9 0x14 shift altgr +Arabic_ghain 0x15 altgr +Arabic_hamzaunderalef 0x15 shift altgr +Arabic_ain 0x16 altgr +grave 0x16 shift altgr +Arabic_ha 0x17 altgr +division 0x17 shift altgr +Arabic_khah 0x18 altgr +multiply 0x18 shift altgr +Arabic_hah 0x19 altgr +Arabic_semicolon 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +Arabic_jeem 0x1a altgr +bracketright 0x1b +braceright 0x1b shift +Arabic_dal 0x1b altgr +Arabic_sheen 0x1e altgr +backslash 0x1e shift altgr +Arabic_seen 0x1f altgr +Arabic_yeh 0x20 altgr +bracketleft 0x20 shift altgr +Arabic_beh 0x21 altgr +bracketright 0x21 shift altgr +Arabic_lam 0x22 altgr +UFEF7 0x22 shift altgr +Arabic_alef 0x23 altgr +Arabic_hamzaonalef 0x23 shift altgr +Arabic_teh 0x24 altgr +Arabic_tatweel 0x24 shift altgr +Arabic_noon 0x25 altgr +Arabic_comma 0x25 shift altgr +Arabic_meem 0x26 altgr +slash 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +Arabic_kaf 0x27 altgr +apostrophe 0x28 +quotedbl 0x28 shift +Arabic_tah 0x28 altgr +grave 0x29 +asciitilde 0x29 shift +Arabic_thal 0x29 altgr +Arabic_shadda 0x29 shift altgr +backslash 0x2b +bar 0x2b shift +less 0x2b altgr +greater 0x2b shift altgr +Arabic_hamzaonyeh 0x2c altgr +asciitilde 0x2c shift altgr +Arabic_hamza 0x2d altgr +Arabic_sukun 0x2d shift altgr +Arabic_hamzaonwaw 0x2e altgr +Arabic_kasra 0x2e shift altgr +Arabic_ra 0x2f altgr +Arabic_kasratan 0x2f shift altgr +UFEFB 0x30 altgr +UFEF5 0x30 shift altgr +Arabic_alefmaksura 0x31 altgr +Arabic_maddaonalef 0x31 shift altgr +Arabic_tehmarbuta 0x32 altgr +apostrophe 0x32 shift altgr +comma 0x33 +less 0x33 shift +Arabic_waw 0x33 altgr +period 0x34 +greater 0x34 shift +Arabic_zain 0x34 altgr +slash 0x35 +question 0x35 shift +Arabic_zah 0x35 altgr +Arabic_question_mark 0x35 shift altgr diff --git a/tools/ioemu/keymaps/common b/tools/ioemu/keymaps/common new file mode 100644 index 0000000000..0b53f1c254 --- /dev/null +++ b/tools/ioemu/keymaps/common @@ -0,0 +1,157 @@ +include modifiers + +# +# Top row +# +1 0x2 +2 0x3 +3 0x4 +4 0x5 +5 0x6 +6 0x7 +7 0x8 +8 0x9 +9 0xa +0 0xb +BackSpace 0xe + +# +# QWERTY first row +# +Tab 0xf localstate +ISO_Left_Tab 0xf shift +q 0x10 addupper +w 0x11 addupper +e 0x12 addupper +r 0x13 addupper +t 0x14 addupper +y 0x15 addupper +u 0x16 addupper +i 0x17 addupper +o 0x18 addupper +p 0x19 addupper + +# +# QWERTY second row +# +a 0x1e addupper +s 0x1f addupper +d 0x20 addupper +f 0x21 addupper +g 0x22 addupper +h 0x23 addupper +j 0x24 addupper +k 0x25 addupper +l 0x26 addupper +Return 0x1c localstate + +# +# QWERTY third row +# +z 0x2c addupper +x 0x2d addupper +c 0x2e addupper +v 0x2f addupper +b 0x30 addupper +n 0x31 addupper +m 0x32 addupper + +space 0x39 localstate + +less 0x56 +greater 0x56 shift +bar 0x56 altgr +brokenbar 0x56 shift altgr + +# +# Esc and Function keys +# +Escape 0x1 localstate +F1 0x3b localstate +F2 0x3c localstate +F3 0x3d localstate +F4 0x3e localstate +F5 0x3f localstate +F6 0x40 localstate +F7 0x41 localstate +F8 0x42 localstate +F9 0x43 localstate +F10 0x44 localstate +F11 0x57 localstate +F12 0x58 localstate + +# Printscreen, Scrollock and Pause +# Printscreen really requires four scancodes (0xe0, 0x2a, 0xe0, 0x37), +# but (0xe0, 0x37) seems to work. +Print 0xb7 localstate +Sys_Req 0xb7 localstate +Execute 0xb7 localstate +Scroll_Lock 0x46 + +# +# Insert - PgDown +# +Insert 0xd2 localstate +Delete 0xd3 localstate +Home 0xc7 localstate +End 0xcf localstate +Page_Up 0xc9 localstate +Page_Down 0xd1 localstate + +# +# Arrow keys +# +Left 0xcb localstate +Up 0xc8 localstate +Down 0xd0 localstate +Right 0xcd localstate + +# +# Numpad +# +Num_Lock 0x45 +KP_Divide 0xb5 +KP_Multiply 0x37 +KP_Subtract 0x4a +KP_Add 0x4e +KP_Enter 0x9c + +KP_Decimal 0x53 numlock +KP_Separator 0x53 numlock +KP_Delete 0x53 + +KP_0 0x52 numlock +KP_Insert 0x52 + +KP_1 0x4f numlock +KP_End 0x4f + +KP_2 0x50 numlock +KP_Down 0x50 + +KP_3 0x51 numlock +KP_Next 0x51 + +KP_4 0x4b numlock +KP_Left 0x4b + +KP_5 0x4c numlock +KP_Begin 0x4c + +KP_6 0x4d numlock +KP_Right 0x4d + +KP_7 0x47 numlock +KP_Home 0x47 + +KP_8 0x48 numlock +KP_Up 0x48 + +KP_9 0x49 numlock +KP_Prior 0x49 + +Caps_Lock 0x3a +# +# Inhibited keys +# +Multi_key 0x0 inhibit diff --git a/tools/ioemu/keymaps/da b/tools/ioemu/keymaps/da new file mode 100644 index 0000000000..3884dcf145 --- /dev/null +++ b/tools/ioemu/keymaps/da @@ -0,0 +1,120 @@ +# generated from XKB map dk +include common +map 0x406 +exclam 0x02 shift +exclamdown 0x02 altgr +onesuperior 0x02 shift altgr +quotedbl 0x03 shift +at 0x03 altgr +twosuperior 0x03 shift altgr +numbersign 0x04 shift +sterling 0x04 altgr +threesuperior 0x04 shift altgr +currency 0x05 shift +dollar 0x05 altgr +onequarter 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +cent 0x06 shift altgr +ampersand 0x07 shift +yen 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +division 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +guillemotleft 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +guillemotright 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +plus 0x0c +question 0x0c shift +plusminus 0x0c altgr +questiondown 0x0c shift altgr +dead_acute 0x0d +dead_grave 0x0d shift +bar 0x0d altgr +brokenbar 0x0d shift altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +cent 0x12 shift altgr +registered 0x13 altgr +thorn 0x14 altgr +THORN 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oe 0x18 altgr +OE 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +aring 0x1a +Aring 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +dead_diaeresis 0x1b +dead_circumflex 0x1b shift +dead_tilde 0x1b altgr +dead_caron 0x1b shift altgr +ordfeminine 0x1e altgr +masculine 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +ae 0x27 +AE 0x27 shift +oslash 0x28 +Ooblique 0x28 shift +dead_caron 0x28 shift altgr +onehalf 0x29 +section 0x29 shift +threequarters 0x29 altgr +paragraph 0x29 shift altgr +apostrophe 0x2b +asterisk 0x2b shift +dead_doubleacute 0x2b altgr +multiply 0x2b shift altgr +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +copyright 0x2e altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +dead_cedilla 0x33 altgr +dead_ogonek 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +dead_abovedot 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +hyphen 0x35 altgr +macron 0x35 shift altgr +nobreakspace 0x39 altgr +less 0x56 +greater 0x56 shift +backslash 0x56 altgr +notsign 0x56 shift altgr diff --git a/tools/ioemu/keymaps/de b/tools/ioemu/keymaps/de new file mode 100644 index 0000000000..ed929c743b --- /dev/null +++ b/tools/ioemu/keymaps/de @@ -0,0 +1,114 @@ +# generated from XKB map de +include common +map 0x407 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +quotedbl 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +section 0x04 shift +threesuperior 0x04 altgr +sterling 0x04 shift altgr +dollar 0x05 shift +onequarter 0x05 altgr +currency 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +ssharp 0x0c +question 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +acute 0x0d +dead_acute 0x0d +grave 0x0d shift +dead_grave 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +z 0x15 addupper +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +udiaeresis 0x1a +Udiaeresis 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +plus 0x1b +asterisk 0x1b shift +asciitilde 0x1b altgr +dead_tilde 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +odiaeresis 0x27 +Odiaeresis 0x27 shift +dead_doubleacute 0x27 altgr +adiaeresis 0x28 +Adiaeresis 0x28 shift +dead_caron 0x28 shift altgr +asciicircum 0x29 +dead_circumflex 0x29 +degree 0x29 shift +notsign 0x29 altgr +numbersign 0x2b +apostrophe 0x2b shift +dead_breve 0x2b shift altgr +y 0x2c addupper +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/tools/ioemu/keymaps/de-ch b/tools/ioemu/keymaps/de-ch new file mode 100644 index 0000000000..f83837b444 --- /dev/null +++ b/tools/ioemu/keymaps/de-ch @@ -0,0 +1,169 @@ +# rdesktop Swiss-German (de-ch) keymap file +# 2003-06-03 by noldi@tristar.ch +# +include common +map 0x00000807 +# +# Scan Code 1 +section 0x29 +degree 0x29 shift +notsign 0x29 altgr inhibit +# +# Scan Code 2 +plus 0x2 shift +brokenbar 0x02 altgr +# +# Scan Code 3 +quotedbl 0x03 shift +at 0x03 altgr +# +# Scan Code 4 +asterisk 0x04 shift +numbersign 0x04 altgr +# +# Scan Code 5 +ccedilla 0x05 shift +onequarter 0x05 altgr inhibit +# +# Scan Code 6 +percent 0x06 shift +onehalf 0x06 altgr inhibit +# +# Scan Code 7 +ampersand 0x07 shift +notsign 0x07 altgr +# +# Scan Code 8 +slash 0x08 shift +bar 0x08 altgr +# +# Scan Code 9 +parenleft 0x09 shift +cent 0x09 altgr +# +# Scan Code 10 +parenright 0x0a shift +# +# Scan Code 11 +equal 0x0b shift +braceright 0x0b altgr inhibit +# +# Scan Code 12 +apostrophe 0x0c +question 0x0c shift +dead_acute 0x0c altgr +# +# Scan Code 13 +dead_circumflex 0x0d +dead_grave 0x0d shift +dead_tilde 0x0d altgr +# +# Scan Code 19 +EuroSign 0x12 altgr +# +# Scan Code 22 +z 0x15 addupper +# +# Scan Code 27 +udiaeresis 0x1a +egrave 0x1a shift +bracketleft 0x1a altgr +# +# Scan Code 28 +dead_diaeresis 0x1b +exclam 0x1b shift +bracketright 0x1b altgr +# +# Scan Code 40 +odiaeresis 0x27 +eacute 0x27 shift +# +# Scan Code 41 +adiaeresis 0x28 +agrave 0x28 shift +braceleft 0x28 altgr +# +# Scan Code 42 (only on international keyboards) +dollar 0x2b +sterling 0x2b shift +braceright 0x2b altgr +# +# Scan Code 45 (only on international keyboards) +backslash 0x56 altgr +# +# Scan Code 46 +y 0x2c addupper +# +# Scan Code 53 +comma 0x33 +semicolon 0x33 shift +# +# Scan Code 54 +period 0x34 +colon 0x34 shift +# +# Scan Code 55 +minus 0x35 +underscore 0x35 shift +# +# Suppress Windows unsupported AltGr keys +# +# Scan Code 17 +paragraph 0x10 altgr inhibit +# +# Scan Code 21 +tslash 0x14 altgr inhibit +# +# Scan Code 22 +leftarrow 0x15 altgr inhibit +# +# Scan Code 23 +downarrow 0x16 altgr inhibit +# +# Scan Code 24 +rightarrow 0x17 altgr inhibit +# +# Scan Code 25 +oslash 0x18 altgr inhibit +# +# Scan Code 26 +thorn 0x19 altgr inhibit +# +# Scan Code 31 +ae 0x1e altgr inhibit +# +# Scan Code 32 +ssharp 0x1f altgr inhibit +# +# Scan Code 33 +eth 0x20 altgr inhibit +# +# Scan Code 34 +dstroke 0x21 altgr inhibit +# +# Scan Code 35 +eng 0x22 altgr inhibit +# +# Scan Code 36 +hstroke 0x23 altgr inhibit +# +# Scan Code 38 +kra 0x25 altgr inhibit +# +# Scan Code 39 +lstroke 0x26 altgr inhibit +# +# Scan Code 46 +guillemotleft 0x2c altgr inhibit +# +# Scan Code 47 +guillemotright 0x2d altgr inhibit +# +# Scan Code 49 +leftdoublequotemark 0x2f altgr inhibit +# +# Scan Code 50 +rightdoublequotemark 0x30 altgr inhibit +# +# Scan Code 52 +mu 0x32 altgr inhibit diff --git a/tools/ioemu/keymaps/en-gb b/tools/ioemu/keymaps/en-gb new file mode 100644 index 0000000000..b45f06c7ce --- /dev/null +++ b/tools/ioemu/keymaps/en-gb @@ -0,0 +1,119 @@ +# generated from XKB map gb +include common +map 0x809 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +quotedbl 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +sterling 0x04 shift +threesuperior 0x04 altgr +dollar 0x05 shift +EuroSign 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +asciicircum 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +ampersand 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +asterisk 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenleft 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +parenright 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +minus 0x0c +underscore 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +equal 0x0d +plus 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +bracketright 0x1b +braceright 0x1b shift +dead_tilde 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +dead_acute 0x27 altgr +dead_doubleacute 0x27 shift altgr +apostrophe 0x28 +at 0x28 shift +dead_circumflex 0x28 altgr +dead_caron 0x28 shift altgr +grave 0x29 +notsign 0x29 shift +bar 0x29 altgr +numbersign 0x2b +asciitilde 0x2b shift +dead_grave 0x2b altgr +dead_breve 0x2b shift altgr +guillemotleft 0x2c altgr +less 0x2c shift altgr +guillemotright 0x2d altgr +greater 0x2d shift altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +less 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +greater 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +slash 0x35 +question 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr +backslash 0x56 +bar 0x56 shift diff --git a/tools/ioemu/keymaps/en-us b/tools/ioemu/keymaps/en-us new file mode 100644 index 0000000000..f5784bbb39 --- /dev/null +++ b/tools/ioemu/keymaps/en-us @@ -0,0 +1,35 @@ +# generated from XKB map us +include common +map 0x409 +exclam 0x02 shift +at 0x03 shift +numbersign 0x04 shift +dollar 0x05 shift +percent 0x06 shift +asciicircum 0x07 shift +ampersand 0x08 shift +asterisk 0x09 shift +parenleft 0x0a shift +parenright 0x0b shift +minus 0x0c +underscore 0x0c shift +equal 0x0d +plus 0x0d shift +bracketleft 0x1a +braceleft 0x1a shift +bracketright 0x1b +braceright 0x1b shift +semicolon 0x27 +colon 0x27 shift +apostrophe 0x28 +quotedbl 0x28 shift +grave 0x29 +asciitilde 0x29 shift +backslash 0x2b +bar 0x2b shift +comma 0x33 +less 0x33 shift +period 0x34 +greater 0x34 shift +slash 0x35 +question 0x35 shift diff --git a/tools/ioemu/keymaps/es b/tools/ioemu/keymaps/es new file mode 100644 index 0000000000..0c29eec5ad --- /dev/null +++ b/tools/ioemu/keymaps/es @@ -0,0 +1,105 @@ +# generated from XKB map es +include common +map 0x40a +exclam 0x02 shift +bar 0x02 altgr +quotedbl 0x03 shift +at 0x03 altgr +oneeighth 0x03 shift altgr +periodcentered 0x04 shift +numbersign 0x04 altgr +sterling 0x04 shift altgr +dollar 0x05 shift +asciitilde 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +notsign 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +seveneighths 0x08 shift altgr +parenleft 0x09 shift +trademark 0x09 shift altgr +parenright 0x0a shift +plusminus 0x0a shift altgr +equal 0x0b shift +degree 0x0b shift altgr +apostrophe 0x0c +question 0x0c shift +exclamdown 0x0d +questiondown 0x0d shift +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +dead_grave 0x1a +dead_circumflex 0x1a shift +bracketleft 0x1a altgr +dead_abovering 0x1a shift altgr +plus 0x1b +asterisk 0x1b shift +bracketright 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +ntilde 0x27 +Ntilde 0x27 shift +dead_doubleacute 0x27 shift altgr +dead_acute 0x28 +dead_diaeresis 0x28 shift +braceleft 0x28 altgr +masculine 0x29 +ordfeminine 0x29 shift +backslash 0x29 altgr +ccedilla 0x2b +Ccedilla 0x2b shift +braceright 0x2b altgr +dead_breve 0x2b shift altgr +guillemotleft 0x2c altgr +less 0x56 +greater 0x56 shift +guillemotright 0x2d altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +division 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/tools/ioemu/keymaps/et b/tools/ioemu/keymaps/et new file mode 100644 index 0000000000..b5a73fef70 --- /dev/null +++ b/tools/ioemu/keymaps/et @@ -0,0 +1,86 @@ +map 0x00000425 +include common + +# +# Top row +# +dead_caron 0x29 +dead_tilde 0x29 shift + +# 1 +exclam 0x2 shift + +# 2 +quotedbl 0x3 shift +at 0x3 altgr + +# 3 +numbersign 0x4 shift +sterling 0x4 altgr +# 4 +currency 0x5 shift +dollar 0x5 altgr +# 5 +percent 0x6 shift +# 6 +ampersand 0x7 shift +# 7 +slash 0x8 shift +braceleft 0x8 altgr +# 8 +parenleft 0x9 shift +bracketleft 0x9 altgr +# 9 +parenright 0xa shift +bracketright 0xa altgr +# 0 +equal 0xb shift +braceright 0xb altgr + +plus 0xc +question 0xc shift +backslash 0xc altgr + +acute 0xd +dead_acute 0xd +grave 0xd shift +dead_grave 0xd shift + +# +# QWERTY first row +# +EuroSign 0x12 altgr +udiaeresis 0x1a +Udiaeresis 0x1a shift +otilde 0x1b +Otilde 0x1b shift +section 0x1b altgr + +# +# QWERTY second row +# +scaron 0x1f altgr +Scaron 0x1f altgr shift +odiaeresis 0x27 +Odiaeresis 0x27 shift +adiaeresis 0x28 +Adiaeresis 0x28 shift +asciicircum 0x28 altgr +apostrophe 0x2b +asterisk 0x2b shift +onehalf 0x2b altgr +# +# QWERTY third row +# +less 0x56 +greater 0x56 shift +bar 0x56 altgr +zcaron 0x2c altgr +Zcaron 0x2c altgr shift +comma 0x33 +semicolon 0x33 shift +period 0x34 +colon 0x34 shift +minus 0x35 +underscore 0x35 shift + diff --git a/tools/ioemu/keymaps/fi b/tools/ioemu/keymaps/fi new file mode 100644 index 0000000000..2a4e0f0454 --- /dev/null +++ b/tools/ioemu/keymaps/fi @@ -0,0 +1,124 @@ +# generated from XKB map se_FI +include common +map 0x40b +exclam 0x02 shift +exclamdown 0x02 altgr +onesuperior 0x02 shift altgr +quotedbl 0x03 shift +at 0x03 altgr +twosuperior 0x03 shift altgr +numbersign 0x04 shift +sterling 0x04 altgr +threesuperior 0x04 shift altgr +currency 0x05 shift +dollar 0x05 altgr +onequarter 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +cent 0x06 shift altgr +ampersand 0x07 shift +yen 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +division 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +guillemotleft 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +guillemotright 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +plus 0x0c +question 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +dead_acute 0x0d +dead_grave 0x0d shift +plusminus 0x0d altgr +notsign 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +cent 0x12 shift altgr +registered 0x13 altgr +thorn 0x14 altgr +THORN 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oe 0x18 altgr +OE 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +aring 0x1a +Aring 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +dead_diaeresis 0x1b +dead_circumflex 0x1b shift +dead_tilde 0x1b altgr +dead_caron 0x1b shift altgr +ordfeminine 0x1e altgr +masculine 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +ampersand 0x25 shift altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +odiaeresis 0x27 +Odiaeresis 0x27 shift +oslash 0x27 altgr +Ooblique 0x27 shift altgr +adiaeresis 0x28 +Adiaeresis 0x28 shift +ae 0x28 altgr +AE 0x28 shift altgr +section 0x29 +onehalf 0x29 shift +paragraph 0x29 altgr +threequarters 0x29 shift altgr +apostrophe 0x2b +asterisk 0x2b shift +acute 0x2b altgr +multiply 0x2b shift altgr +guillemotleft 0x2c altgr +less 0x2c shift altgr +guillemotright 0x2d altgr +greater 0x2d shift altgr +copyright 0x2e altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +apostrophe 0x30 shift altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +dead_cedilla 0x33 altgr +dead_ogonek 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +dead_abovedot 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +hyphen 0x35 altgr +macron 0x35 shift altgr +nobreakspace 0x39 altgr diff --git a/tools/ioemu/keymaps/fo b/tools/ioemu/keymaps/fo new file mode 100644 index 0000000000..83add423c6 --- /dev/null +++ b/tools/ioemu/keymaps/fo @@ -0,0 +1,77 @@ +map 0x438 +include common + +# +# Top row +# +onehalf 0x29 +section 0x29 shift + +# 1 +exclam 0x2 shift + +# 2 +quotedbl 0x3 shift +at 0x3 altgr + +# 3 +numbersign 0x4 shift +sterling 0x4 altgr +# 4 +currency 0x5 shift +dollar 0x5 altgr +# 5 +percent 0x6 shift +# 6 +ampersand 0x7 shift +# 7 +slash 0x8 shift +braceleft 0x8 altgr +# 8 +parenleft 0x9 shift +bracketleft 0x9 altgr +# 9 +parenright 0xa shift +bracketright 0xa altgr +# 0 +equal 0xb shift +braceright 0xb altgr + +plus 0xc +question 0xc shift +plusminus 0xc altgr + +bar 0xd altgr +dead_acute 0xd + +# +# QWERTY first row +# +EuroSign 0x12 altgr +aring 0x1a +Aring 0x1a shift +eth 0x1b addupper +asciitilde 0x1b altgr + +# +# QWERTY second row +# +ae 0x27 addupper +oslash 0x28 +Ooblique 0x28 shift +apostrophe 0x2b +asterisk 0x2b shift + +# +# QWERTY third row +# +less 0x56 +greater 0x56 shift +backslash 0x56 altgr +comma 0x33 +semicolon 0x33 shift +period 0x34 +colon 0x34 shift +minus 0x35 +underscore 0x35 shift + diff --git a/tools/ioemu/keymaps/fr b/tools/ioemu/keymaps/fr new file mode 100644 index 0000000000..cbb45910f4 --- /dev/null +++ b/tools/ioemu/keymaps/fr @@ -0,0 +1,181 @@ +include common +map 0x40c +# +# Top row +# +twosuperior 0x29 +notsign 0x29 altgr + +ampersand 0x02 +1 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr + +eacute 0x03 +2 0x03 shift +asciitilde 0x03 altgr +oneeighth 0x03 shift altgr + +quotedbl 0x04 +3 0x04 shift +numbersign 0x04 altgr + +apostrophe 0x05 +4 0x05 shift +braceleft 0x05 altgr + +parenleft 0x06 +5 0x06 shift +bracketleft 0x06 altgr +threeeighths 0x06 shift altgr + +minus 0x07 +6 0x07 shift +bar 0x07 altgr +fiveeighths 0x07 shift altgr + +egrave 0x08 +7 0x08 shift +grave 0x08 altgr +seveneighths 0x08 shift altgr + +underscore 0x09 +8 0x09 shift +backslash 0x09 altgr +trademark 0x09 shift altgr + +ccedilla 0x0a +9 0x0a shift +asciicircum 0x0a altgr +plusminus 0x0a shift altgr + +agrave 0x0b +0 0x0b shift +at 0x0b altgr + +parenright 0x0c +degree 0x0c shift +bracketright 0x0c altgr +questiondown 0x0c shift altgr + +equal 0x0d +plus 0x0d shift +braceright 0x0d altgr +dead_ogonek 0x0d shift altgr + +# +# AZERTY first row +# + +a 0x10 addupper +ae 0x10 altgr +AE 0x10 shift altgr + +z 0x11 addupper +guillemotleft 0x11 altgr + +EuroSign 0x12 altgr + +paragraph 0x13 altgr +registered 0x13 shift altgr + +tslash 0x14 altgr +Tslash 0x14 shift altgr + +leftarrow 0x15 altgr +yen 0x15 shift altgr + +downarrow 0x16 altgr +uparrow 0x16 shift altgr + +rightarrow 0x17 altgr +idotless 0x17 shift altgr + +oslash 0x18 altgr +Ooblique 0x18 shift altgr + +thorn 0x19 altgr +THORN 0x19 shift altgr + +dead_circumflex 0x1a +dead_diaeresis 0x1a shift +dead_abovering 0x1a shift altgr + +dollar 0x1b +sterling 0x1b shift +currency 0x1b altgr +dead_macron 0x1b shift altgr + +# +# AZERTY second row +# +q 0x1e addupper +Greek_OMEGA 0x1e shift altgr + +ssharp 0x1f altgr + +eth 0x20 altgr +ETH 0x20 shift altgr + +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr + +eng 0x22 altgr +ENG 0x22 shift altgr + +hstroke 0x23 altgr +Hstroke 0x23 shift altgr + +kra 0x25 altgr + +lstroke 0x26 altgr +Lstroke 0x26 shift altgr + +m 0x27 addupper +masculine 0x27 shift altgr + +ugrave 0x28 +percent 0x28 shift +dead_caron 0x28 shift altgr + +asterisk 0x2b +mu 0x2b shift +dead_grave 0x2b altgr +dead_breve 0x2b shift altgr + +# +# AZERTY third row +# +less 0x56 +greater 0x56 shift + +w 0x2c addupper + +guillemotright 0x2d altgr + +cent 0x2e altgr +copyright 0x2e shift altgr + +leftdoublequotemark 0x2f altgr + +rightdoublequotemark 0x30 altgr + +comma 0x32 +question 0x32 shift +dead_acute 0x32 altgr +dead_doubleacute 0x32 shift altgr + +semicolon 0x33 +period 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr + +colon 0x34 +slash 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr + +exclam 0x35 +section 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/tools/ioemu/keymaps/fr-be b/tools/ioemu/keymaps/fr-be new file mode 100644 index 0000000000..92d668eb61 --- /dev/null +++ b/tools/ioemu/keymaps/fr-be @@ -0,0 +1,140 @@ +# generated from XKB map be +include common +map 0x80c +ampersand 0x02 +1 0x02 shift +bar 0x02 altgr +exclamdown 0x02 shift altgr +eacute 0x03 +2 0x03 shift +at 0x03 altgr +oneeighth 0x03 shift altgr +quotedbl 0x04 +3 0x04 shift +numbersign 0x04 altgr +sterling 0x04 shift altgr +apostrophe 0x05 +4 0x05 shift +onequarter 0x05 altgr +dollar 0x05 shift altgr +parenleft 0x06 +5 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +section 0x07 +6 0x07 shift +asciicircum 0x07 altgr +fiveeighths 0x07 shift altgr +egrave 0x08 +7 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +exclam 0x09 +8 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +ccedilla 0x0a +9 0x0a shift +braceleft 0x0a altgr +plusminus 0x0a shift altgr +agrave 0x0b +0 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +parenright 0x0c +degree 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +minus 0x0d +underscore 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +a 0x10 addupper +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +z 0x11 addupper +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +cent 0x12 shift altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +dead_circumflex 0x1a +dead_diaeresis 0x1a shift +bracketleft 0x1a altgr +dead_abovering 0x1a shift altgr +dollar 0x1b +asterisk 0x1b shift +bracketright 0x1b altgr +dead_macron 0x1b shift altgr +q 0x1e addupper +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +ampersand 0x25 shift altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +m 0x27 addupper +dead_acute 0x27 altgr +dead_doubleacute 0x27 shift altgr +ugrave 0x28 +percent 0x28 shift +dead_acute 0x28 altgr +dead_caron 0x28 shift altgr +twosuperior 0x29 +threesuperior 0x29 shift +notsign 0x29 altgr +mu 0x2b +sterling 0x2b shift +dead_grave 0x2b altgr +dead_breve 0x2b shift altgr +w 0x2c addupper +guillemotleft 0x2c altgr +less 0x2c shift altgr +guillemotright 0x2d altgr +greater 0x2d shift altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +apostrophe 0x30 shift altgr +comma 0x32 +question 0x32 shift +dead_cedilla 0x32 altgr +masculine 0x32 shift altgr +semicolon 0x33 +period 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +colon 0x34 +slash 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +equal 0x35 +plus 0x35 shift +dead_tilde 0x35 altgr +dead_abovedot 0x35 shift altgr +backslash 0x56 altgr diff --git a/tools/ioemu/keymaps/fr-ca b/tools/ioemu/keymaps/fr-ca new file mode 100644 index 0000000000..b645208e42 --- /dev/null +++ b/tools/ioemu/keymaps/fr-ca @@ -0,0 +1,50 @@ +# Canadian French +# By Simon Germain +include common +map 0xc0c + +backslash 0x29 altgr +plusminus 0x2 altgr +at 0x3 altgr +sterling 0x4 altgr +cent 0x5 altgr +currency 0x6 altgr +notsign 0x7 altgr +bar 0x29 shift +twosuperior 0x9 altgr +threesuperior 0xa altgr +onequarter 0xb altgr +onehalf 0xc altgr +threequarters 0xd altgr +section 0x18 altgr +paragraph 0x19 altgr +bracketleft 0x1a altgr +bracketright 0x1b altgr +asciitilde 0x27 altgr +braceleft 0x28 altgr +braceright 0x2b altgr +less 0x2b +greater 0x2b shift +guillemotleft 0x56 +guillemotright 0x56 shift +degree 0x56 altgr +mu 0x32 altgr +eacute 0x35 +dead_acute 0x35 altgr +dead_grave 0x28 +dead_circumflex 0x1a +dead_circumflex 0x1a shift +dead_cedilla 0x1b +dead_diaeresis 0x1b shift +exclam 0x2 shift +quotedbl 0x3 shift +slash 0x4 shift +dollar 0x5 shift +percent 0x6 shift +question 0x7 shift +ampersand 0x8 shift +asterisk 0x9 shift +parenleft 0xa shift +parenright 0xb shift +underscore 0xc shift +plus 0xd shift diff --git a/tools/ioemu/keymaps/fr-ch b/tools/ioemu/keymaps/fr-ch new file mode 100644 index 0000000000..4620d2033b --- /dev/null +++ b/tools/ioemu/keymaps/fr-ch @@ -0,0 +1,114 @@ +# generated from XKB map fr_CH +include common +map 0x100c +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +quotedbl 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +section 0x04 shift +threesuperior 0x04 altgr +sterling 0x04 shift altgr +dollar 0x05 shift +onequarter 0x05 altgr +currency 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +ssharp 0x0c +question 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +acute 0x0d +dead_acute 0x0d +grave 0x0d shift +dead_grave 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +z 0x15 addupper +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +udiaeresis 0x1a +Udiaeresis 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +plus 0x1b +asterisk 0x1b shift +asciitilde 0x1b altgr +dead_tilde 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +odiaeresis 0x27 +Odiaeresis 0x27 shift +dead_doubleacute 0x27 altgr +adiaeresis 0x28 +Adiaeresis 0x28 shift +dead_caron 0x28 shift altgr +asciicircum 0x29 +dead_circumflex 0x29 +degree 0x29 shift +notsign 0x29 altgr +numbersign 0x2b +apostrophe 0x2b shift +dead_breve 0x2b shift altgr +y 0x2c addupper +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/tools/ioemu/keymaps/hr b/tools/ioemu/keymaps/hr new file mode 100644 index 0000000000..613aa6925d --- /dev/null +++ b/tools/ioemu/keymaps/hr @@ -0,0 +1,125 @@ +# generated from XKB map hr +include common +map 0x41a +exclam 0x02 shift +asciitilde 0x02 altgr +dead_tilde 0x02 shift altgr +quotedbl 0x03 shift +dead_caron 0x03 altgr +caron 0x03 shift altgr +numbersign 0x04 shift +asciicircum 0x04 altgr +dead_circumflex 0x04 shift altgr +dollar 0x05 shift +dead_breve 0x05 altgr +breve 0x05 shift altgr +percent 0x06 shift +degree 0x06 altgr +dead_abovering 0x06 shift altgr +ampersand 0x07 shift +dead_ogonek 0x07 altgr +ogonek 0x07 shift altgr +slash 0x08 shift +grave 0x08 altgr +dead_grave 0x08 shift altgr +parenleft 0x09 shift +dead_abovedot 0x09 altgr +abovedot 0x09 shift altgr +parenright 0x0a shift +dead_acute 0x0a altgr +apostrophe 0x0a shift altgr +equal 0x0b shift +dead_doubleacute 0x0b altgr +doubleacute 0x0b shift altgr +apostrophe 0x0c +question 0x0c shift +dead_diaeresis 0x0c altgr +diaeresis 0x0c shift altgr +plus 0x0d +asterisk 0x0d shift +dead_cedilla 0x0d altgr +cedilla 0x0d shift altgr +backslash 0x10 altgr +Greek_OMEGA 0x10 shift altgr +bar 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +z 0x15 addupper +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +scaron 0x1a +Scaron 0x1a shift +division 0x1a altgr +dead_abovering 0x1a shift altgr +dstroke 0x1b +Dstroke 0x1b shift +multiply 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +bracketleft 0x21 altgr +ordfeminine 0x21 shift altgr +bracketright 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +lstroke 0x25 altgr +ampersand 0x25 shift altgr +Lstroke 0x26 altgr +ccaron 0x27 +Ccaron 0x27 shift +dead_acute 0x27 altgr +dead_doubleacute 0x27 shift altgr +cacute 0x28 +Cacute 0x28 shift +ssharp 0x28 altgr +dead_caron 0x28 shift altgr +dead_cedilla 0x29 +dead_diaeresis 0x29 shift +notsign 0x29 altgr +zcaron 0x2b +Zcaron 0x2b shift +currency 0x2b altgr +dead_breve 0x2b shift altgr +y 0x2c addupper +guillemotleft 0x2c altgr +less 0x2c shift altgr +guillemotright 0x2d altgr +greater 0x2d shift altgr +cent 0x2e altgr +copyright 0x2e shift altgr +at 0x2f altgr +grave 0x2f shift altgr +braceleft 0x30 altgr +apostrophe 0x30 shift altgr +braceright 0x31 altgr +section 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/tools/ioemu/keymaps/hu b/tools/ioemu/keymaps/hu new file mode 100644 index 0000000000..8aba444417 --- /dev/null +++ b/tools/ioemu/keymaps/hu @@ -0,0 +1,115 @@ +# Hungarian keyboard layout (QWERTZ) +# Created by: The NeverGone + +include common +map 0x40e + + +# AltGr keys: +notsign 0x29 altgr +asciitilde 0x02 altgr +caron 0x03 altgr +asciicircum 0x04 altgr +breve 0x05 altgr +degree 0x06 altgr +ogonek 0x07 altgr +grave 0x08 altgr +abovedot 0x09 altgr +acute 0x0a altgr +doubleacute 0x0b altgr +diaeresis 0x0c altgr +cedilla 0x0d altgr +backslash 0x10 altgr +bar 0x11 altgr +EuroSign 0x12 altgr +Iacute 0x17 altgr +division 0x1a altgr +multiply 0x1b altgr +dstroke 0x1f altgr +Dstroke 0x20 altgr +bracketleft 0x21 altgr +bracketright 0x22 altgr +iacute 0x24 altgr +lstroke 0x25 altgr +Lstroke 0x26 altgr +dollar 0x27 altgr +ssharp 0x28 altgr +currency 0x2b altgr +less 0x56 altgr +greater 0x2c altgr +numbersign 0x2d altgr +ampersand 0x2e altgr +at 0x2f altgr +braceleft 0x30 altgr +braceright 0x31 altgr +semicolon 0x33 altgr +asterisk 0x35 altgr + + +# Shift keys: +section 0x29 shift +apostrophe 0x02 shift +quotedbl 0x03 shift +plus 0x04 shift +exclam 0x05 shift +percent 0x06 shift +slash 0x07 shift +equal 0x08 shift +parenleft 0x09 shift +parenright 0x0a shift +Odiaeresis 0x0b shift +Udiaeresis 0x0c shift +Oacute 0x0d shift +Z 0x15 shift +Odoubleacute 0x1a shift +Uacute 0x1b shift +Eacute 0x27 shift +Aacute 0x28 shift +Udoubleacute 0x2b shift +Y 0x2c shift +question 0x33 shift +colon 0x34 shift +underscore 0x35 shift +F13 0x3b shift +F14 0x3c shift +F15 0x3d shift +F16 0x3e shift +F17 0x3f shift +F18 0x40 shift +F19 0x41 shift +F20 0x42 shift +F21 0x43 shift +F22 0x44 shift +F23 0x57 shift +F24 0x58 shift + + +# Ctrl keys: +F25 0x3b ctrl +F26 0x3c ctrl +F27 0x3d ctrl +F28 0x3e ctrl +F29 0x3f ctrl +F30 0x40 ctrl +F31 0x41 ctrl +F32 0x42 ctrl +F33 0x43 ctrl +F34 0x44 ctrl +F35 0x57 ctrl +#NoSymbol 0x58 ctrl + + +0 0x29 +odiaeresis 0x0b +udiaeresis 0x0c +oacute 0x0d +z 0x15 +odoubleacute 0x1a +uacute 0x1b +eacute 0x27 +aacute 0x28 +udoubleacute 0x2b +y 0x2c +comma 0x33 +period 0x34 +minus 0x35 diff --git a/tools/ioemu/keymaps/is b/tools/ioemu/keymaps/is new file mode 100644 index 0000000000..8fde40f19a --- /dev/null +++ b/tools/ioemu/keymaps/is @@ -0,0 +1,140 @@ +# 2004-03-16 Halldór Guðmundsson and Morten Lange +# Keyboard definition file for the Icelandic keyboard +# to be used in rdesktop 1.3.x ( See rdesktop.org) +# generated from XKB map de, and changed manually +# Location for example /usr/local/share/rdesktop/keymaps/is +include common +map 0x40f +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +quotedbl 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +#section 0x04 shift +numbersign 0x04 shift +threesuperior 0x04 altgr +sterling 0x04 shift altgr +dollar 0x05 shift +onequarter 0x05 altgr +currency 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +#ssharp 0x0c +odiaeresis 0x0c +#question 0x0c shift +Odiaeresis 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +#acute 0x0d +minus 0x0d +#dead_acute 0x0d +#grave 0x0d shift +#dead_grave 0x0d shift +underscore 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +#z 0x15 addupper +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +#thorn 0x19 altgr +#THORN 0x19 shift altgr +#udiaeresis 0x1a +#Udiaeresis 0x1a shift +#dead_diaeresis 0x1a altgr +#dead_abovering 0x1a shift altgr +eth 0x1a +ETH 0x1a shift +apostrophe 0x1b +question 0x1b shift +#plus 0x1b +#asterisk 0x1b shift +asciitilde 0x1b altgr +#grave 0x1b altgr +#dead_tilde 0x1b altgr +#dead_macron 0x1b shift altgr +#ae 0x1e altgr +#AE 0x1e shift altgr +#eth 0x20 altgr +#eth 0x20 +#ETH 0x20 shift altgr +#ETH 0x20 shift +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +#adiaeresis 0x27 +#Adiaeresis 0x27 shift +ae 0x27 +AE 0x27 shift +dead_doubleacute 0x27 altgr +#adiaeresis 0x28 +#Adiaeresis 0x28 shift +#dead_caron 0x28 shift altgr +#asciicircum 0x29 +acute 0x28 +dead_acute 0x28 +#dead_circumflex 0x29 +#degree 0x29 shift +#notsign 0x29 altgr +plus 0x2b +asterisk 0x2b shift +grave 0x2b altgr +#numbersign 0x2b +#apostrophe 0x2b shift +#dead_breve 0x2b shift altgr +#y 0x2c addupper +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +#minus 0x35 +#underscore 0x35 shift +thorn 0x35 +THORN 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr + diff --git a/tools/ioemu/keymaps/it b/tools/ioemu/keymaps/it new file mode 100644 index 0000000000..00ca73a3e2 --- /dev/null +++ b/tools/ioemu/keymaps/it @@ -0,0 +1,115 @@ +# generated from XKB map it +include common +map 0x410 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +quotedbl 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +sterling 0x04 shift +threesuperior 0x04 altgr +dollar 0x05 shift +onequarter 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +parenleft 0x09 shift +trademark 0x09 shift altgr +parenright 0x0a shift +plusminus 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +apostrophe 0x0c +question 0x0c shift +grave 0x0c altgr +questiondown 0x0c shift altgr +igrave 0x0d +asciicircum 0x0d shift +asciitilde 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +cent 0x12 shift altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +egrave 0x1a +eacute 0x1a shift +bracketleft 0x1a altgr +dead_abovering 0x1a shift altgr +plus 0x1b +asterisk 0x1b shift +bracketright 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +ograve 0x27 +ccedilla 0x27 shift +at 0x27 altgr +dead_doubleacute 0x27 shift altgr +agrave 0x28 +degree 0x28 shift +numbersign 0x28 altgr +backslash 0x29 +bar 0x29 shift +notsign 0x29 altgr +ugrave 0x2b +section 0x2b shift +dead_grave 0x2b altgr +dead_breve 0x2b shift altgr +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/tools/ioemu/keymaps/ja b/tools/ioemu/keymaps/ja new file mode 100644 index 0000000000..8fd0b9ef1b --- /dev/null +++ b/tools/ioemu/keymaps/ja @@ -0,0 +1,104 @@ +# generated from XKB map jp106 +include common +map 0x411 +exclam 0x02 shift +kana_NU 0x02 altgr +quotedbl 0x03 shift +kana_FU 0x03 altgr +numbersign 0x04 shift +kana_A 0x04 altgr +kana_a 0x04 shift altgr +dollar 0x05 shift +kana_U 0x05 altgr +kana_u 0x05 shift altgr +percent 0x06 shift +kana_E 0x06 altgr +kana_e 0x06 shift altgr +ampersand 0x07 shift +kana_O 0x07 altgr +kana_o 0x07 shift altgr +apostrophe 0x08 shift +kana_YA 0x08 altgr +kana_ya 0x08 shift altgr +parenleft 0x09 shift +kana_YU 0x09 altgr +kana_yu 0x09 shift altgr +parenright 0x0a shift +kana_YO 0x0a altgr +kana_yo 0x0a shift altgr +asciitilde 0x0b shift +kana_WA 0x0b altgr +kana_WO 0x0b shift altgr +minus 0x0c +equal 0x0c shift +kana_HO 0x0c altgr +asciicircum 0x0d +asciitilde 0x0d shift +kana_HE 0x0d altgr +kana_TA 0x10 altgr +kana_TE 0x11 altgr +kana_I 0x12 altgr +kana_i 0x12 shift altgr +kana_SU 0x13 altgr +kana_KA 0x14 altgr +kana_N 0x15 altgr +kana_NA 0x16 altgr +kana_NI 0x17 altgr +kana_RA 0x18 altgr +kana_SE 0x19 altgr +at 0x1a +grave 0x1a shift +voicedsound 0x1a altgr +bracketleft 0x1b +braceleft 0x1b shift +semivoicedsound 0x1b altgr +kana_openingbracket 0x1b shift altgr +kana_CHI 0x1e altgr +kana_TO 0x1f altgr +kana_SHI 0x20 altgr +kana_HA 0x21 altgr +kana_KI 0x22 altgr +kana_KU 0x23 altgr +kana_MA 0x24 altgr +kana_NO 0x25 altgr +kana_RI 0x26 altgr +semicolon 0x27 +plus 0x27 shift +kana_RE 0x27 altgr +colon 0x28 +asterisk 0x28 shift +kana_KE 0x28 altgr +Zenkaku_Hankaku 0x29 +bracketright 0x2b +braceright 0x2b shift +kana_MU 0x2b altgr +kana_closingbracket 0x2b shift altgr +kana_TSU 0x2c altgr +kana_tsu 0x2c shift altgr +kana_SA 0x2d altgr +kana_SO 0x2e altgr +kana_HI 0x2f altgr +kana_KO 0x30 altgr +kana_MI 0x31 altgr +kana_MO 0x32 altgr +comma 0x33 +less 0x33 shift +kana_NE 0x33 altgr +kana_comma 0x33 shift altgr +period 0x34 +greater 0x34 shift +kana_RU 0x34 altgr +kana_fullstop 0x34 shift altgr +slash 0x35 +question 0x35 shift +kana_ME 0x35 altgr +kana_conjunctive 0x35 shift altgr +Eisu_toggle 0x3a shift +Execute 0x54 shift +Kanji 0x70 +backslash 0x73 +bar 0x7d shift +underscore 0x73 shift +Henkan_Mode 0x79 +Katakana 0x70 +Muhenkan 0x7b diff --git a/tools/ioemu/keymaps/lt b/tools/ioemu/keymaps/lt new file mode 100644 index 0000000000..3d9d619ea5 --- /dev/null +++ b/tools/ioemu/keymaps/lt @@ -0,0 +1,57 @@ +# generated from XKB map lt +include common +map 0x427 +exclam 0x02 shift +aogonek 0x02 altgr +Aogonek 0x02 shift altgr +at 0x03 shift +ccaron 0x03 altgr +Ccaron 0x03 shift altgr +numbersign 0x04 shift +eogonek 0x04 altgr +Eogonek 0x04 shift altgr +dollar 0x05 shift +eabovedot 0x05 altgr +Eabovedot 0x05 shift altgr +percent 0x06 shift +iogonek 0x06 altgr +Iogonek 0x06 shift altgr +asciicircum 0x07 shift +scaron 0x07 altgr +Scaron 0x07 shift altgr +ampersand 0x08 shift +uogonek 0x08 altgr +Uogonek 0x08 shift altgr +asterisk 0x09 shift +umacron 0x09 altgr +Umacron 0x09 shift altgr +parenleft 0x0a shift +doublelowquotemark 0x0a altgr +parenright 0x0b shift +leftdoublequotemark 0x0b altgr +minus 0x0c +underscore 0x0c shift +equal 0x0d +plus 0x0d shift +zcaron 0x0d altgr +Zcaron 0x0d shift altgr +bracketleft 0x1a +braceleft 0x1a shift +bracketright 0x1b +braceright 0x1b shift +semicolon 0x27 +colon 0x27 shift +apostrophe 0x28 +quotedbl 0x28 shift +grave 0x29 +asciitilde 0x29 shift +backslash 0x2b +bar 0x2b shift +comma 0x33 +less 0x33 shift +period 0x34 +greater 0x34 shift +slash 0x35 +question 0x35 shift +endash 0x56 +EuroSign 0x56 shift diff --git a/tools/ioemu/keymaps/lv b/tools/ioemu/keymaps/lv new file mode 100644 index 0000000000..1d91727912 --- /dev/null +++ b/tools/ioemu/keymaps/lv @@ -0,0 +1,128 @@ +# generated from XKB map lv +include common +map 0x426 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +at 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +numbersign 0x04 shift +threesuperior 0x04 altgr +sterling 0x04 shift altgr +dollar 0x05 shift +EuroSign 0x05 altgr +cent 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +asciicircum 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +ampersand 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +asterisk 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenleft 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +parenright 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +minus 0x0c +underscore 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +equal 0x0d +plus 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +emacron 0x12 altgr +Emacron 0x12 shift altgr +rcedilla 0x13 altgr +Rcedilla 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +umacron 0x16 altgr +Umacron 0x16 shift altgr +imacron 0x17 altgr +Imacron 0x17 shift altgr +omacron 0x18 altgr +Omacron 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +bracketright 0x1b +braceright 0x1b shift +dead_tilde 0x1b altgr +dead_macron 0x1b shift altgr +ISO_Next_Group 0x1c shift +amacron 0x1e altgr +Amacron 0x1e shift altgr +scaron 0x1f altgr +Scaron 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +gcedilla 0x22 altgr +Gcedilla 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kcedilla 0x25 altgr +Kcedilla 0x25 shift altgr +lcedilla 0x26 altgr +Lcedilla 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +dead_acute 0x27 altgr +dead_doubleacute 0x27 shift altgr +apostrophe 0x28 +quotedbl 0x28 shift +leftdoublequotemark 0x28 altgr +doublelowquotemark 0x28 shift altgr +grave 0x29 +asciitilde 0x29 shift +notsign 0x29 altgr +backslash 0x2b +bar 0x2b shift +dead_grave 0x2b altgr +dead_breve 0x2b shift altgr +zcaron 0x2c altgr +Zcaron 0x2c shift altgr +guillemotright 0x2d altgr +greater 0x2d shift altgr +ccaron 0x2e altgr +Ccaron 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +apostrophe 0x30 shift altgr +ncedilla 0x31 altgr +Ncedilla 0x31 shift altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +less 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +greater 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +slash 0x35 +question 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr +nobreakspace 0x39 altgr diff --git a/tools/ioemu/keymaps/mk b/tools/ioemu/keymaps/mk new file mode 100644 index 0000000000..18c1504842 --- /dev/null +++ b/tools/ioemu/keymaps/mk @@ -0,0 +1,101 @@ +# generated from XKB map mk +include common +map 0x42f +exclam 0x02 shift +at 0x03 shift +doublelowquotemark 0x03 shift altgr +numbersign 0x04 shift +leftdoublequotemark 0x04 shift altgr +dollar 0x05 shift +percent 0x06 shift +asciicircum 0x07 shift +ampersand 0x08 shift +asterisk 0x09 shift +parenleft 0x0a shift +parenright 0x0b shift +minus 0x0c +underscore 0x0c shift +equal 0x0d +plus 0x0d shift +Cyrillic_lje 0x10 altgr +Cyrillic_LJE 0x10 shift altgr +Cyrillic_nje 0x11 altgr +Cyrillic_NJE 0x11 shift altgr +Cyrillic_ie 0x12 altgr +Cyrillic_IE 0x12 shift altgr +Cyrillic_er 0x13 altgr +Cyrillic_ER 0x13 shift altgr +Cyrillic_te 0x14 altgr +Cyrillic_TE 0x14 shift altgr +Macedonia_dse 0x15 altgr +Macedonia_DSE 0x15 shift altgr +Cyrillic_u 0x16 altgr +Cyrillic_U 0x16 shift altgr +Cyrillic_i 0x17 altgr +Cyrillic_I 0x17 shift altgr +Cyrillic_o 0x18 altgr +Cyrillic_O 0x18 shift altgr +Cyrillic_pe 0x19 altgr +Cyrillic_PE 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +Cyrillic_sha 0x1a altgr +Cyrillic_SHA 0x1a shift altgr +bracketright 0x1b +braceright 0x1b shift +Macedonia_gje 0x1b altgr +Macedonia_GJE 0x1b shift altgr +Cyrillic_a 0x1e altgr +Cyrillic_A 0x1e shift altgr +Cyrillic_es 0x1f altgr +Cyrillic_ES 0x1f shift altgr +Cyrillic_de 0x20 altgr +Cyrillic_DE 0x20 shift altgr +Cyrillic_ef 0x21 altgr +Cyrillic_EF 0x21 shift altgr +Cyrillic_ghe 0x22 altgr +Cyrillic_GHE 0x22 shift altgr +Cyrillic_ha 0x23 altgr +Cyrillic_HA 0x23 shift altgr +Cyrillic_je 0x24 altgr +Cyrillic_JE 0x24 shift altgr +Cyrillic_ka 0x25 altgr +Cyrillic_KA 0x25 shift altgr +Cyrillic_el 0x26 altgr +Cyrillic_EL 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +Cyrillic_che 0x27 altgr +Cyrillic_CHE 0x27 shift altgr +apostrophe 0x28 +quotedbl 0x28 shift +Macedonia_kje 0x28 altgr +Macedonia_KJE 0x28 shift altgr +grave 0x29 +asciitilde 0x29 shift +backslash 0x2b +bar 0x2b shift +Cyrillic_zhe 0x2b altgr +Cyrillic_ZHE 0x2b shift altgr +Cyrillic_ze 0x2c altgr +Cyrillic_ZE 0x2c shift altgr +Cyrillic_dzhe 0x2d altgr +Cyrillic_DZHE 0x2d shift altgr +Cyrillic_tse 0x2e altgr +Cyrillic_TSE 0x2e shift altgr +Cyrillic_ve 0x2f altgr +Cyrillic_VE 0x2f shift altgr +Cyrillic_be 0x30 altgr +Cyrillic_BE 0x30 shift altgr +Cyrillic_en 0x31 altgr +Cyrillic_EN 0x31 shift altgr +Cyrillic_em 0x32 altgr +Cyrillic_EM 0x32 shift altgr +comma 0x33 +less 0x33 shift +semicolon 0x33 shift altgr +period 0x34 +greater 0x34 shift +colon 0x34 shift altgr +slash 0x35 +question 0x35 shift diff --git a/tools/ioemu/keymaps/modifiers b/tools/ioemu/keymaps/modifiers new file mode 100644 index 0000000000..d8b019f040 --- /dev/null +++ b/tools/ioemu/keymaps/modifiers @@ -0,0 +1,17 @@ +Shift_R 0x36 +Shift_L 0x2a + +Alt_R 0xb8 +Mode_switch 0xb8 +Alt_L 0x38 + +Control_R 0x9d +Control_L 0x1d + +# Translate Super to Windows keys. +# This is hardcoded. See documentation for details. +Super_R 0xdb +Super_L 0xdc + +# Translate Menu to the Windows Application key. +Menu 0xdd diff --git a/tools/ioemu/keymaps/nl b/tools/ioemu/keymaps/nl new file mode 100644 index 0000000000..bc823bd2f7 --- /dev/null +++ b/tools/ioemu/keymaps/nl @@ -0,0 +1,60 @@ +# Dutch (Netherlands) +include common +map 0x413 + +exclam 0x02 shift +onesuperior 0x02 altgr +quotebl 0x03 shift +twosuperior 0x03 altgr +numbersign 0x04 shift +threesuperior 0x04 altgr +dollar 0x05 shift +onequarter 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +ampersand 0x07 shift +threequarters 0x07 altgr +underscore 0x08 shift +sterling 0x08 altgr +parenleft 0x09 shift +braceleft 0x09 altgr +parenright 0x0a shift +braceright 0x0a altgr +apostrophe 0x0b shift +slash 0x0c +question 0x0c shift +backslash 0x0c altgr +degree 0x0d +dead_tilde 0x0d shift +dead_cedilla 0x0d altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +dead_diaeresis 0x1a +dead_circumflex 0x1a shift +asterisk 0x1b +bar 0x1b shift +ssharp 0x1f altgr +plus 0x27 +plusminus 0x27 shift +dead_acute 0x28 +dead_grave 0x28 shift +at 0x29 +section 0x29 shift +notsign 0x29 altgr +less 0x2b +greater 0x2b shift +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +copyright 0x2e altgr +mu 0x32 altgr +comma 0x33 +semicolon 0x33 shift +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +hyphen 0x35 +equal 0x35 shift +bracketright 0x56 +bracketleft 0x56 shift +brokenbar 0x56 altgr + diff --git a/tools/ioemu/keymaps/nl-be b/tools/ioemu/keymaps/nl-be new file mode 100644 index 0000000000..34fc881ad0 --- /dev/null +++ b/tools/ioemu/keymaps/nl-be @@ -0,0 +1,3 @@ +# Dutch (Belgium) +map 0x813 +include common diff --git a/tools/ioemu/keymaps/no b/tools/ioemu/keymaps/no new file mode 100644 index 0000000000..40a64790d1 --- /dev/null +++ b/tools/ioemu/keymaps/no @@ -0,0 +1,119 @@ +# generated from XKB map no +include common +map 0x414 +exclam 0x02 shift +exclamdown 0x02 altgr +onesuperior 0x02 shift altgr +quotedbl 0x03 shift +at 0x03 altgr +twosuperior 0x03 shift altgr +numbersign 0x04 shift +sterling 0x04 altgr +threesuperior 0x04 shift altgr +currency 0x05 shift +dollar 0x05 altgr +onequarter 0x05 shift altgr +percent 0x06 shift +onehalf 0x06 altgr +cent 0x06 shift altgr +ampersand 0x07 shift +yen 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +division 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +guillemotleft 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +guillemotright 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +plus 0x0c +question 0x0c shift +plusminus 0x0c altgr +questiondown 0x0c shift altgr +backslash 0x0d +dead_grave 0x0d shift +dead_acute 0x0d altgr +notsign 0x0d shift altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +cent 0x12 shift altgr +registered 0x13 altgr +thorn 0x14 altgr +THORN 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oe 0x18 altgr +OE 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +aring 0x1a +Aring 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +dead_diaeresis 0x1b +dead_circumflex 0x1b shift +asciicircum 0x01b shift +dead_tilde 0x1b altgr +asciitilde 0x1b altgr +dead_caron 0x1b shift altgr +ordfeminine 0x1e altgr +masculine 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +oslash 0x27 +Ooblique 0x27 shift +dead_doubleacute 0x27 shift altgr +ae 0x28 +AE 0x28 shift +dead_caron 0x28 shift altgr +bar 0x29 +section 0x29 shift +brokenbar 0x29 altgr +paragraph 0x29 shift altgr +apostrophe 0x2b +asterisk 0x2b shift +multiply 0x2b shift altgr +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +copyright 0x2e altgr +leftdoublequotemark 0x2f altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +dead_cedilla 0x33 altgr +dead_ogonek 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +dead_abovedot 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +hyphen 0x35 altgr +macron 0x35 shift altgr +nobreakspace 0x39 altgr +onehalf 0x56 altgr +threequarters 0x56 shift altgr diff --git a/tools/ioemu/keymaps/pl b/tools/ioemu/keymaps/pl new file mode 100644 index 0000000000..09c600d355 --- /dev/null +++ b/tools/ioemu/keymaps/pl @@ -0,0 +1,122 @@ +# generated from XKB map pl +include common +map 0x415 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +at 0x03 shift +twosuperior 0x03 altgr +oneeighth 0x03 shift altgr +numbersign 0x04 shift +threesuperior 0x04 altgr +sterling 0x04 shift altgr +dollar 0x05 shift +onequarter 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +asciicircum 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +ampersand 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +asterisk 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenleft 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +parenright 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +minus 0x0c +underscore 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +equal 0x0d +plus 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +eogonek 0x12 altgr +Eogonek 0x12 shift altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +EuroSign 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oacute 0x18 altgr +Oacute 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +bracketright 0x1b +braceright 0x1b shift +dead_tilde 0x1b altgr +dead_macron 0x1b shift altgr +aogonek 0x1e altgr +Aogonek 0x1e shift altgr +sacute 0x1f altgr +Sacute 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +dead_acute 0x27 altgr +dead_doubleacute 0x27 shift altgr +apostrophe 0x28 +quotedbl 0x28 shift +dead_circumflex 0x28 altgr +dead_caron 0x28 shift altgr +grave 0x29 +asciitilde 0x29 shift +notsign 0x29 altgr +backslash 0x2b +bar 0x2b shift +dead_grave 0x2b altgr +dead_breve 0x2b shift altgr +zabovedot 0x2c altgr +Zabovedot 0x2c shift altgr +zacute 0x2d altgr +Zacute 0x2d shift altgr +cacute 0x2e altgr +Cacute 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +nacute 0x31 altgr +Nacute 0x31 shift altgr +mu 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +less 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +greater 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +slash 0x35 +question 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/tools/ioemu/keymaps/pt b/tools/ioemu/keymaps/pt new file mode 100644 index 0000000000..c6941f651c --- /dev/null +++ b/tools/ioemu/keymaps/pt @@ -0,0 +1,113 @@ +# generated from XKB map pt +include common +map 0x816 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +quotedbl 0x03 shift +at 0x03 altgr +oneeighth 0x03 shift altgr +numbersign 0x04 shift +sterling 0x04 altgr +dollar 0x05 shift +section 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +threequarters 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +apostrophe 0x0c +question 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +guillemotleft 0x0d +guillemotright 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +cent 0x12 shift altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +plus 0x1a +asterisk 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +dead_acute 0x1b +dead_grave 0x1b shift +dead_tilde 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +ccedilla 0x27 +Ccedilla 0x27 shift +dead_doubleacute 0x27 shift altgr +masculine 0x28 +ordfeminine 0x28 shift +dead_circumflex 0x28 altgr +dead_caron 0x28 shift altgr +backslash 0x29 +bar 0x29 shift +notsign 0x29 altgr +dead_tilde 0x2b +dead_circumflex 0x2b shift +dead_breve 0x2b shift altgr +less 0x56 +greater 0x56 shift +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +mu 0x32 altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +multiply 0x33 shift altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +division 0x34 shift altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/tools/ioemu/keymaps/pt-br b/tools/ioemu/keymaps/pt-br new file mode 100644 index 0000000000..54bafc5dc3 --- /dev/null +++ b/tools/ioemu/keymaps/pt-br @@ -0,0 +1,69 @@ +# generated from XKB map br +include common +map 0x416 +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +at 0x03 shift +twosuperior 0x03 altgr +onehalf 0x03 shift altgr +numbersign 0x04 shift +threesuperior 0x04 altgr +threequarters 0x04 shift altgr +dollar 0x05 shift +sterling 0x05 altgr +onequarter 0x05 shift altgr +percent 0x06 shift +cent 0x06 altgr +dead_diaeresis 0x07 shift +notsign 0x07 altgr +diaeresis 0x07 shift altgr +ampersand 0x08 shift +braceleft 0x08 altgr +asterisk 0x09 shift +bracketleft 0x09 altgr +parenleft 0x0a shift +bracketright 0x0a altgr +parenright 0x0b shift +braceright 0x0b altgr +minus 0x0c +underscore 0x0c shift +backslash 0x0c altgr +equal 0x0d +plus 0x0d shift +section 0x0d altgr +EuroSign 0x12 altgr +registered 0x13 altgr +dead_acute 0x1a +dead_grave 0x1a shift +acute 0x1a altgr +grave 0x1a shift altgr +bracketleft 0x1b +braceleft 0x1b shift +ordfeminine 0x1b altgr +ccedilla 0x27 +Ccedilla 0x27 shift +dead_tilde 0x28 +dead_circumflex 0x28 shift +asciitilde 0x28 altgr +asciicircum 0x28 shift altgr +apostrophe 0x29 +quotedbl 0x29 shift +bracketright 0x2b +braceright 0x2b shift +masculine 0x2b altgr +copyright 0x2e altgr +mu 0x32 altgr +comma 0x33 +less 0x33 shift +period 0x34 +greater 0x34 shift +semicolon 0x35 +colon 0x35 shift +comma 0x53 numlock +backslash 0x56 +bar 0x56 shift +slash 0x73 +question 0x73 shift +degree 0x73 altgr +KP_Decimal 0x34 diff --git a/tools/ioemu/keymaps/ru b/tools/ioemu/keymaps/ru new file mode 100644 index 0000000000..b3e7d24de5 --- /dev/null +++ b/tools/ioemu/keymaps/ru @@ -0,0 +1,109 @@ +# generated from XKB map ru +include common +map 0x419 +exclam 0x02 shift +at 0x03 shift +quotedbl 0x03 shift altgr +numbersign 0x04 shift +dollar 0x05 shift +asterisk 0x05 shift altgr +percent 0x06 shift +colon 0x06 shift altgr +asciicircum 0x07 shift +comma 0x07 shift altgr +ampersand 0x08 shift +period 0x08 shift altgr +asterisk 0x09 shift +semicolon 0x09 shift altgr +parenleft 0x0a shift +parenright 0x0b shift +minus 0x0c +underscore 0x0c shift +equal 0x0d +plus 0x0d shift +Cyrillic_shorti 0x10 altgr +Cyrillic_SHORTI 0x10 shift altgr +Cyrillic_tse 0x11 altgr +Cyrillic_TSE 0x11 shift altgr +Cyrillic_u 0x12 altgr +Cyrillic_U 0x12 shift altgr +Cyrillic_ka 0x13 altgr +Cyrillic_KA 0x13 shift altgr +Cyrillic_ie 0x14 altgr +Cyrillic_IE 0x14 shift altgr +Cyrillic_en 0x15 altgr +Cyrillic_EN 0x15 shift altgr +Cyrillic_ghe 0x16 altgr +Cyrillic_GHE 0x16 shift altgr +Cyrillic_sha 0x17 altgr +Cyrillic_SHA 0x17 shift altgr +Cyrillic_shcha 0x18 altgr +Cyrillic_SHCHA 0x18 shift altgr +Cyrillic_ze 0x19 altgr +Cyrillic_ZE 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +Cyrillic_ha 0x1a altgr +Cyrillic_HA 0x1a shift altgr +bracketright 0x1b +braceright 0x1b shift +Cyrillic_hardsign 0x1b altgr +Cyrillic_HARDSIGN 0x1b shift altgr +Cyrillic_ef 0x1e altgr +Cyrillic_EF 0x1e shift altgr +Cyrillic_yeru 0x1f altgr +Cyrillic_YERU 0x1f shift altgr +Cyrillic_ve 0x20 altgr +Cyrillic_VE 0x20 shift altgr +Cyrillic_a 0x21 altgr +Cyrillic_A 0x21 shift altgr +Cyrillic_pe 0x22 altgr +Cyrillic_PE 0x22 shift altgr +Cyrillic_er 0x23 altgr +Cyrillic_ER 0x23 shift altgr +Cyrillic_o 0x24 altgr +Cyrillic_O 0x24 shift altgr +Cyrillic_el 0x25 altgr +Cyrillic_EL 0x25 shift altgr +Cyrillic_de 0x26 altgr +Cyrillic_DE 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +Cyrillic_zhe 0x27 altgr +Cyrillic_ZHE 0x27 shift altgr +apostrophe 0x28 +quotedbl 0x28 shift +Cyrillic_e 0x28 altgr +Cyrillic_E 0x28 shift altgr +grave 0x29 +asciitilde 0x29 shift +Cyrillic_io 0x29 altgr +Cyrillic_IO 0x29 shift altgr +backslash 0x2b +bar 0x2b shift +Cyrillic_ya 0x2c altgr +Cyrillic_YA 0x2c shift altgr +Cyrillic_che 0x2d altgr +Cyrillic_CHE 0x2d shift altgr +Cyrillic_es 0x2e altgr +Cyrillic_ES 0x2e shift altgr +Cyrillic_em 0x2f altgr +Cyrillic_EM 0x2f shift altgr +Cyrillic_i 0x30 altgr +Cyrillic_I 0x30 shift altgr +Cyrillic_te 0x31 altgr +Cyrillic_TE 0x31 shift altgr +Cyrillic_softsign 0x32 altgr +Cyrillic_SOFTSIGN 0x32 shift altgr +comma 0x33 +less 0x33 shift +Cyrillic_be 0x33 altgr +Cyrillic_BE 0x33 shift altgr +period 0x34 +greater 0x34 shift +Cyrillic_yu 0x34 altgr +Cyrillic_YU 0x34 shift altgr +slash 0x35 +question 0x35 shift +slash 0x56 altgr +bar 0x56 shift altgr diff --git a/tools/ioemu/keymaps/sl b/tools/ioemu/keymaps/sl new file mode 100644 index 0000000000..56835a92c3 --- /dev/null +++ b/tools/ioemu/keymaps/sl @@ -0,0 +1,110 @@ +# generated from XKB map sl +include common +map 0x424 +exclam 0x02 shift +asciitilde 0x02 altgr +dead_tilde 0x02 shift altgr +quotedbl 0x03 shift +dead_caron 0x03 altgr +caron 0x03 shift altgr +numbersign 0x04 shift +asciicircum 0x04 altgr +dead_circumflex 0x04 shift altgr +dollar 0x05 shift +dead_breve 0x05 altgr +breve 0x05 shift altgr +percent 0x06 shift +degree 0x06 altgr +dead_abovering 0x06 shift altgr +ampersand 0x07 shift +dead_ogonek 0x07 altgr +ogonek 0x07 shift altgr +slash 0x08 shift +grave 0x08 altgr +dead_grave 0x08 shift altgr +parenleft 0x09 shift +dead_abovedot 0x09 altgr +abovedot 0x09 shift altgr +parenright 0x0a shift +dead_acute 0x0a altgr +equal 0x0b shift +dead_doubleacute 0x0b altgr +doubleacute 0x0b shift altgr +apostrophe 0x0c +question 0x0c shift +dead_diaeresis 0x0c altgr +diaeresis 0x0c shift altgr +plus 0x0d +asterisk 0x0d shift +dead_cedilla 0x0d altgr +cedilla 0x0d shift altgr +backslash 0x10 altgr +Greek_OMEGA 0x10 shift altgr +bar 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +z 0x15 addupper +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +rightarrow 0x17 altgr +idotless 0x17 shift altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +scaron 0x1a +Scaron 0x1a shift +division 0x1a altgr +dstroke 0x1b +Dstroke 0x1b shift +multiply 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +bracketleft 0x21 altgr +ordfeminine 0x21 shift altgr +bracketright 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +lstroke 0x25 altgr +Lstroke 0x26 altgr +ccaron 0x27 +Ccaron 0x27 shift +cacute 0x28 +Cacute 0x28 shift +ssharp 0x28 altgr +dead_cedilla 0x29 +notsign 0x29 altgr +zcaron 0x2b +Zcaron 0x2b shift +currency 0x2b altgr +y 0x2c addupper +guillemotleft 0x2c altgr +guillemotright 0x2d altgr +cent 0x2e altgr +copyright 0x2e shift altgr +at 0x2f altgr +braceleft 0x30 altgr +braceright 0x31 altgr +section 0x32 altgr +masculine 0x32 shift altgr +comma 0x33 +semicolon 0x33 shift +horizconnector 0x33 altgr +period 0x34 +colon 0x34 shift +periodcentered 0x34 altgr +minus 0x35 +underscore 0x35 shift +dead_belowdot 0x35 altgr diff --git a/tools/ioemu/keymaps/sv b/tools/ioemu/keymaps/sv new file mode 100644 index 0000000000..736d637b3f --- /dev/null +++ b/tools/ioemu/keymaps/sv @@ -0,0 +1,82 @@ +map 0x0000041d +include common + +# +# Top row +# +section 0x29 +onehalf 0x29 shift + +# 1 +exclam 0x2 shift + +# 2 +quotedbl 0x3 shift +at 0x3 altgr + +# 3 +numbersign 0x4 shift +sterling 0x4 altgr +# 4 +currency 0x5 shift +dollar 0x5 altgr +# 5 +percent 0x6 shift +# 6 +ampersand 0x7 shift +# 7 +slash 0x8 shift +braceleft 0x8 altgr +# 8 +parenleft 0x9 shift +bracketleft 0x9 altgr +# 9 +parenright 0xa shift +bracketright 0xa altgr +# 0 +equal 0xb shift +braceright 0xb altgr + +plus 0xc +question 0xc shift +backslash 0xc altgr + +acute 0xd +dead_acute 0xd +grave 0xd shift +dead_grave 0xd shift + +# +# QWERTY first row +# +EuroSign 0x12 altgr +aring 0x1a +Aring 0x1a shift +dead_diaeresis 0x1b +dead_circumflex 0x1b shift +dead_tilde 0x1b altgr + +# +# QWERTY second row +# +odiaeresis 0x27 +Odiaeresis 0x27 shift +adiaeresis 0x28 +Adiaeresis 0x28 shift +apostrophe 0x2b +asterisk 0x2b shift + +# +# QWERTY third row +# +less 0x56 +greater 0x56 shift +bar 0x56 altgr +mu 0x32 altgr +comma 0x33 +semicolon 0x33 shift +period 0x34 +colon 0x34 shift +minus 0x35 +underscore 0x35 shift + diff --git a/tools/ioemu/keymaps/th b/tools/ioemu/keymaps/th new file mode 100644 index 0000000000..b65b6da5d9 --- /dev/null +++ b/tools/ioemu/keymaps/th @@ -0,0 +1,131 @@ +# generated from XKB map th +include common +map 0x41e +exclam 0x02 shift +Thai_lakkhangyao 0x02 altgr +plus 0x02 shift altgr +at 0x03 shift +slash 0x03 altgr +Thai_leknung 0x03 shift altgr +numbersign 0x04 shift +minus 0x04 altgr +Thai_leksong 0x04 shift altgr +dollar 0x05 shift +Thai_phosamphao 0x05 altgr +Thai_leksam 0x05 shift altgr +percent 0x06 shift +Thai_thothung 0x06 altgr +Thai_leksi 0x06 shift altgr +asciicircum 0x07 shift +Thai_sarau 0x07 altgr +Thai_sarauu 0x07 shift altgr +ampersand 0x08 shift +Thai_saraue 0x08 altgr +Thai_baht 0x08 shift altgr +asterisk 0x09 shift +Thai_khokhwai 0x09 altgr +Thai_lekha 0x09 shift altgr +parenleft 0x0a shift +Thai_totao 0x0a altgr +Thai_lekhok 0x0a shift altgr +parenright 0x0b shift +Thai_chochan 0x0b altgr +Thai_lekchet 0x0b shift altgr +minus 0x0c +underscore 0x0c shift +Thai_khokhai 0x0c altgr +Thai_lekpaet 0x0c shift altgr +equal 0x0d +plus 0x0d shift +Thai_chochang 0x0d altgr +Thai_lekkao 0x0d shift altgr +Thai_maiyamok 0x10 altgr +Thai_leksun 0x10 shift altgr +Thai_saraaimaimalai 0x11 altgr +quotedbl 0x11 shift altgr +Thai_saraam 0x12 altgr +Thai_dochada 0x12 shift altgr +Thai_phophan 0x13 altgr +Thai_thonangmontho 0x13 shift altgr +Thai_saraa 0x14 altgr +Thai_thothong 0x14 shift altgr +Thai_maihanakat 0x15 altgr +Thai_nikhahit 0x15 shift altgr +Thai_saraii 0x16 altgr +Thai_maitri 0x16 shift altgr +Thai_rorua 0x17 altgr +Thai_nonen 0x17 shift altgr +Thai_nonu 0x18 altgr +Thai_paiyannoi 0x18 shift altgr +Thai_yoyak 0x19 altgr +Thai_yoying 0x19 shift altgr +bracketleft 0x1a +braceleft 0x1a shift +Thai_bobaimai 0x1a altgr +Thai_thothan 0x1a shift altgr +bracketright 0x1b +braceright 0x1b shift +Thai_loling 0x1b altgr +comma 0x1b shift altgr +Thai_fofan 0x1e altgr +Thai_ru 0x1e shift altgr +Thai_hohip 0x1f altgr +Thai_khorakhang 0x1f shift altgr +Thai_kokai 0x20 altgr +Thai_topatak 0x20 shift altgr +Thai_dodek 0x21 altgr +Thai_sarao 0x21 shift altgr +Thai_sarae 0x22 altgr +Thai_chochoe 0x22 shift altgr +Thai_maitho 0x23 altgr +Thai_maitaikhu 0x23 shift altgr +Thai_maiek 0x24 altgr +Thai_maichattawa 0x24 shift altgr +Thai_saraaa 0x25 altgr +Thai_sorusi 0x25 shift altgr +Thai_sosua 0x26 altgr +Thai_sosala 0x26 shift altgr +semicolon 0x27 +colon 0x27 shift +Thai_wowaen 0x27 altgr +Thai_soso 0x27 shift altgr +apostrophe 0x28 +quotedbl 0x28 shift +Thai_ngongu 0x28 altgr +period 0x28 shift altgr +grave 0x29 +asciitilde 0x29 shift +underscore 0x29 altgr +percent 0x29 shift altgr +ISO_First_Group 0x2a shift +backslash 0x2b +bar 0x2b shift +Thai_khokhuat 0x2b altgr +Thai_khokhon 0x2b shift altgr +Thai_phophung 0x2c altgr +parenleft 0x2c shift altgr +Thai_popla 0x2d altgr +parenright 0x2d shift altgr +Thai_saraae 0x2e altgr +Thai_choching 0x2e shift altgr +Thai_oang 0x2f altgr +Thai_honokhuk 0x2f shift altgr +Thai_sarai 0x30 altgr +Thai_phinthu 0x30 shift altgr +Thai_sarauee 0x31 altgr +Thai_thanthakhat 0x31 shift altgr +Thai_thothahan 0x32 altgr +question 0x32 shift altgr +comma 0x33 +less 0x33 shift +Thai_moma 0x33 altgr +Thai_thophuthao 0x33 shift altgr +period 0x34 +greater 0x34 shift +Thai_saraaimaimuan 0x34 altgr +Thai_lochula 0x34 shift altgr +slash 0x35 +question 0x35 shift +Thai_fofa 0x35 altgr +Thai_lu 0x35 shift altgr +ISO_Last_Group 0x36 shift diff --git a/tools/ioemu/keymaps/tr b/tools/ioemu/keymaps/tr new file mode 100644 index 0000000000..5650e1e93f --- /dev/null +++ b/tools/ioemu/keymaps/tr @@ -0,0 +1,123 @@ +# generated from XKB map tr +include common +map 0x41f +exclam 0x02 shift +onesuperior 0x02 altgr +exclamdown 0x02 shift altgr +apostrophe 0x03 shift +at 0x03 altgr +oneeighth 0x03 shift altgr +dead_circumflex 0x04 shift +numbersign 0x04 altgr +sterling 0x04 shift altgr +plus 0x05 shift +dollar 0x05 altgr +percent 0x06 shift +onehalf 0x06 altgr +threeeighths 0x06 shift altgr +ampersand 0x07 shift +asciicircum 0x07 altgr +fiveeighths 0x07 shift altgr +slash 0x08 shift +braceleft 0x08 altgr +seveneighths 0x08 shift altgr +parenleft 0x09 shift +bracketleft 0x09 altgr +trademark 0x09 shift altgr +parenright 0x0a shift +bracketright 0x0a altgr +plusminus 0x0a shift altgr +equal 0x0b shift +braceright 0x0b altgr +degree 0x0b shift altgr +asterisk 0x0c +question 0x0c shift +backslash 0x0c altgr +questiondown 0x0c shift altgr +minus 0x0d +underscore 0x0d shift +dead_cedilla 0x0d altgr +dead_ogonek 0x0d shift altgr +at 0x10 altgr +Greek_OMEGA 0x10 shift altgr +lstroke 0x11 altgr +Lstroke 0x11 shift altgr +EuroSign 0x12 altgr +paragraph 0x13 altgr +registered 0x13 shift altgr +tslash 0x14 altgr +Tslash 0x14 shift altgr +leftarrow 0x15 altgr +yen 0x15 shift altgr +downarrow 0x16 altgr +uparrow 0x16 shift altgr +idotless 0x17 +I 0x17 shift +rightarrow 0x17 altgr +oslash 0x18 altgr +Ooblique 0x18 shift altgr +thorn 0x19 altgr +THORN 0x19 shift altgr +gbreve 0x1a +Gbreve 0x1a shift +dead_diaeresis 0x1a altgr +dead_abovering 0x1a shift altgr +udiaeresis 0x1b +Udiaeresis 0x1b shift +asciitilde 0x1b altgr +dead_macron 0x1b shift altgr +ae 0x1e altgr +AE 0x1e shift altgr +ssharp 0x1f altgr +section 0x1f shift altgr +eth 0x20 altgr +ETH 0x20 shift altgr +dstroke 0x21 altgr +ordfeminine 0x21 shift altgr +eng 0x22 altgr +ENG 0x22 shift altgr +hstroke 0x23 altgr +Hstroke 0x23 shift altgr +kra 0x25 altgr +ampersand 0x25 shift altgr +lstroke 0x26 altgr +Lstroke 0x26 shift altgr +scedilla 0x27 +Scedilla 0x27 shift +dead_acute 0x27 altgr +dead_doubleacute 0x27 shift altgr +i 0x28 +Iabovedot 0x28 shift +dead_circumflex 0x28 altgr +dead_caron 0x28 shift altgr +backslash 0x29 +quotedbl 0x29 shift +asciitilde 0x29 altgr +comma 0x2b +semicolon 0x2b shift +bar 0x2b altgr +dead_breve 0x2b shift altgr +guillemotleft 0x2c altgr +less 0x2c shift altgr +guillemotright 0x2d altgr +greater 0x2d shift altgr +cent 0x2e altgr +copyright 0x2e shift altgr +leftdoublequotemark 0x2f altgr +grave 0x2f shift altgr +rightdoublequotemark 0x30 altgr +apostrophe 0x30 shift altgr +mu 0x32 altgr +masculine 0x32 shift altgr +odiaeresis 0x33 +Odiaeresis 0x33 shift +less 0x33 altgr +multiply 0x33 shift altgr +ccedilla 0x34 +Ccedilla 0x34 shift +greater 0x34 altgr +division 0x34 shift altgr +period 0x35 +colon 0x35 shift +dead_belowdot 0x35 altgr +dead_abovedot 0x35 shift altgr diff --git a/tools/ioemu/kqemu.c b/tools/ioemu/kqemu.c new file mode 100644 index 0000000000..bd70474d69 --- /dev/null +++ b/tools/ioemu/kqemu.c @@ -0,0 +1,900 @@ +/* + * KQEMU support + * + * Copyright (c) 2005 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "config.h" +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "exec-all.h" + +#ifdef USE_KQEMU + +#define DEBUG +//#define PROFILE + +#include +#include +#include "kqemu.h" + +/* compatibility stuff */ +#ifndef KQEMU_RET_SYSCALL +#define KQEMU_RET_SYSCALL 0x0300 /* syscall insn */ +#endif +#ifndef KQEMU_MAX_RAM_PAGES_TO_UPDATE +#define KQEMU_MAX_RAM_PAGES_TO_UPDATE 512 +#define KQEMU_RAM_PAGES_UPDATE_ALL (KQEMU_MAX_RAM_PAGES_TO_UPDATE + 1) +#endif +#ifndef KQEMU_MAX_MODIFIED_RAM_PAGES +#define KQEMU_MAX_MODIFIED_RAM_PAGES 512 +#endif + +#ifdef _WIN32 +#define KQEMU_DEVICE "\\\\.\\kqemu" +#else +#define KQEMU_DEVICE "/dev/kqemu" +#endif + +#ifdef _WIN32 +#define KQEMU_INVALID_FD INVALID_HANDLE_VALUE +HANDLE kqemu_fd = KQEMU_INVALID_FD; +#define kqemu_closefd(x) CloseHandle(x) +#else +#define KQEMU_INVALID_FD -1 +int kqemu_fd = KQEMU_INVALID_FD; +#define kqemu_closefd(x) close(x) +#endif + +/* 0 = not allowed + 1 = user kqemu + 2 = kernel kqemu +*/ +int kqemu_allowed = 1; +unsigned long *pages_to_flush; +unsigned int nb_pages_to_flush; +unsigned long *ram_pages_to_update; +unsigned int nb_ram_pages_to_update; +unsigned long *modified_ram_pages; +unsigned int nb_modified_ram_pages; +uint8_t *modified_ram_pages_table; +extern uint32_t **l1_phys_map; + +#define cpuid(index, eax, ebx, ecx, edx) \ + asm volatile ("cpuid" \ + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) \ + : "0" (index)) + +#ifdef __x86_64__ +static int is_cpuid_supported(void) +{ + return 1; +} +#else +static int is_cpuid_supported(void) +{ + int v0, v1; + asm volatile ("pushf\n" + "popl %0\n" + "movl %0, %1\n" + "xorl $0x00200000, %0\n" + "pushl %0\n" + "popf\n" + "pushf\n" + "popl %0\n" + : "=a" (v0), "=d" (v1) + : + : "cc"); + return (v0 != v1); +} +#endif + +static void kqemu_update_cpuid(CPUState *env) +{ + int critical_features_mask, features; + uint32_t eax, ebx, ecx, edx; + + /* the following features are kept identical on the host and + target cpus because they are important for user code. Strictly + speaking, only SSE really matters because the OS must support + it if the user code uses it. */ + critical_features_mask = + CPUID_CMOV | CPUID_CX8 | + CPUID_FXSR | CPUID_MMX | CPUID_SSE | + CPUID_SSE2 | CPUID_SEP; + if (!is_cpuid_supported()) { + features = 0; + } else { + cpuid(1, eax, ebx, ecx, edx); + features = edx; + } +#ifdef __x86_64__ + /* NOTE: on x86_64 CPUs, SYSENTER is not supported in + compatibility mode, so in order to have the best performances + it is better not to use it */ + features &= ~CPUID_SEP; +#endif + env->cpuid_features = (env->cpuid_features & ~critical_features_mask) | + (features & critical_features_mask); + /* XXX: we could update more of the target CPUID state so that the + non accelerated code sees exactly the same CPU features as the + accelerated code */ +} + +int kqemu_init(CPUState *env) +{ + struct kqemu_init init; + int ret, version; +#ifdef _WIN32 + DWORD temp; +#endif + + if (!kqemu_allowed) + return -1; + +#ifdef _WIN32 + kqemu_fd = CreateFile(KQEMU_DEVICE, GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + NULL); +#else + kqemu_fd = open(KQEMU_DEVICE, O_RDWR); +#endif + if (kqemu_fd == KQEMU_INVALID_FD) { + fprintf(stderr, "Could not open '%s' - QEMU acceleration layer not activated\n", KQEMU_DEVICE); + return -1; + } + version = 0; +#ifdef _WIN32 + DeviceIoControl(kqemu_fd, KQEMU_GET_VERSION, NULL, 0, + &version, sizeof(version), &temp, NULL); +#else + ioctl(kqemu_fd, KQEMU_GET_VERSION, &version); +#endif + if (version != KQEMU_VERSION) { + fprintf(stderr, "Version mismatch between kqemu module and qemu (%08x %08x) - disabling kqemu use\n", + version, KQEMU_VERSION); + goto fail; + } + + pages_to_flush = qemu_vmalloc(KQEMU_MAX_PAGES_TO_FLUSH * + sizeof(unsigned long)); + if (!pages_to_flush) + goto fail; + + ram_pages_to_update = qemu_vmalloc(KQEMU_MAX_RAM_PAGES_TO_UPDATE * + sizeof(unsigned long)); + if (!ram_pages_to_update) + goto fail; + + modified_ram_pages = qemu_vmalloc(KQEMU_MAX_MODIFIED_RAM_PAGES * + sizeof(unsigned long)); + if (!modified_ram_pages) + goto fail; + modified_ram_pages_table = qemu_mallocz(phys_ram_size >> TARGET_PAGE_BITS); + if (!modified_ram_pages_table) + goto fail; + + init.ram_base = phys_ram_base; + init.ram_size = phys_ram_size; + init.ram_dirty = phys_ram_dirty; + init.phys_to_ram_map = l1_phys_map; + init.pages_to_flush = pages_to_flush; +#if KQEMU_VERSION >= 0x010200 + init.ram_pages_to_update = ram_pages_to_update; +#endif +#if KQEMU_VERSION >= 0x010300 + init.modified_ram_pages = modified_ram_pages; +#endif +#ifdef _WIN32 + ret = DeviceIoControl(kqemu_fd, KQEMU_INIT, &init, sizeof(init), + NULL, 0, &temp, NULL) == TRUE ? 0 : -1; +#else + ret = ioctl(kqemu_fd, KQEMU_INIT, &init); +#endif + if (ret < 0) { + fprintf(stderr, "Error %d while initializing QEMU acceleration layer - disabling it for now\n", ret); + fail: + kqemu_closefd(kqemu_fd); + kqemu_fd = KQEMU_INVALID_FD; + return -1; + } + kqemu_update_cpuid(env); + env->kqemu_enabled = kqemu_allowed; + nb_pages_to_flush = 0; + nb_ram_pages_to_update = 0; + return 0; +} + +void kqemu_flush_page(CPUState *env, target_ulong addr) +{ +#if defined(DEBUG) + if (loglevel & CPU_LOG_INT) { + fprintf(logfile, "kqemu_flush_page: addr=" TARGET_FMT_lx "\n", addr); + } +#endif + if (nb_pages_to_flush >= KQEMU_MAX_PAGES_TO_FLUSH) + nb_pages_to_flush = KQEMU_FLUSH_ALL; + else + pages_to_flush[nb_pages_to_flush++] = addr; +} + +void kqemu_flush(CPUState *env, int global) +{ +#ifdef DEBUG + if (loglevel & CPU_LOG_INT) { + fprintf(logfile, "kqemu_flush:\n"); + } +#endif + nb_pages_to_flush = KQEMU_FLUSH_ALL; +} + +void kqemu_set_notdirty(CPUState *env, ram_addr_t ram_addr) +{ +#ifdef DEBUG + if (loglevel & CPU_LOG_INT) { + fprintf(logfile, "kqemu_set_notdirty: addr=%08lx\n", ram_addr); + } +#endif + /* we only track transitions to dirty state */ + if (phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] != 0xff) + return; + if (nb_ram_pages_to_update >= KQEMU_MAX_RAM_PAGES_TO_UPDATE) + nb_ram_pages_to_update = KQEMU_RAM_PAGES_UPDATE_ALL; + else + ram_pages_to_update[nb_ram_pages_to_update++] = ram_addr; +} + +static void kqemu_reset_modified_ram_pages(void) +{ + int i; + unsigned long page_index; + + for(i = 0; i < nb_modified_ram_pages; i++) { + page_index = modified_ram_pages[i] >> TARGET_PAGE_BITS; + modified_ram_pages_table[page_index] = 0; + } + nb_modified_ram_pages = 0; +} + +void kqemu_modify_page(CPUState *env, ram_addr_t ram_addr) +{ + unsigned long page_index; + int ret; +#ifdef _WIN32 + DWORD temp; +#endif + + page_index = ram_addr >> TARGET_PAGE_BITS; + if (!modified_ram_pages_table[page_index]) { +#if 0 + printf("%d: modify_page=%08lx\n", nb_modified_ram_pages, ram_addr); +#endif + modified_ram_pages_table[page_index] = 1; + modified_ram_pages[nb_modified_ram_pages++] = ram_addr; + if (nb_modified_ram_pages >= KQEMU_MAX_MODIFIED_RAM_PAGES) { + /* flush */ +#ifdef _WIN32 + ret = DeviceIoControl(kqemu_fd, KQEMU_MODIFY_RAM_PAGES, + &nb_modified_ram_pages, + sizeof(nb_modified_ram_pages), + NULL, 0, &temp, NULL); +#else + ret = ioctl(kqemu_fd, KQEMU_MODIFY_RAM_PAGES, + &nb_modified_ram_pages); +#endif + kqemu_reset_modified_ram_pages(); + } + } +} + +struct fpstate { + uint16_t fpuc; + uint16_t dummy1; + uint16_t fpus; + uint16_t dummy2; + uint16_t fptag; + uint16_t dummy3; + + uint32_t fpip; + uint32_t fpcs; + uint32_t fpoo; + uint32_t fpos; + uint8_t fpregs1[8 * 10]; +}; + +struct fpxstate { + uint16_t fpuc; + uint16_t fpus; + uint16_t fptag; + uint16_t fop; + uint32_t fpuip; + uint16_t cs_sel; + uint16_t dummy0; + uint32_t fpudp; + uint16_t ds_sel; + uint16_t dummy1; + uint32_t mxcsr; + uint32_t mxcsr_mask; + uint8_t fpregs1[8 * 16]; + uint8_t xmm_regs[16 * 16]; + uint8_t dummy2[96]; +}; + +static struct fpxstate fpx1 __attribute__((aligned(16))); + +static void restore_native_fp_frstor(CPUState *env) +{ + int fptag, i, j; + struct fpstate fp1, *fp = &fp1; + + fp->fpuc = env->fpuc; + fp->fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + fptag = 0; + for (i=7; i>=0; i--) { + fptag <<= 2; + if (env->fptags[i]) { + fptag |= 3; + } else { + /* the FPU automatically computes it */ + } + } + fp->fptag = fptag; + j = env->fpstt; + for(i = 0;i < 8; i++) { + memcpy(&fp->fpregs1[i * 10], &env->fpregs[j].d, 10); + j = (j + 1) & 7; + } + asm volatile ("frstor %0" : "=m" (*fp)); +} + +static void save_native_fp_fsave(CPUState *env) +{ + int fptag, i, j; + uint16_t fpuc; + struct fpstate fp1, *fp = &fp1; + + asm volatile ("fsave %0" : : "m" (*fp)); + env->fpuc = fp->fpuc; + env->fpstt = (fp->fpus >> 11) & 7; + env->fpus = fp->fpus & ~0x3800; + fptag = fp->fptag; + for(i = 0;i < 8; i++) { + env->fptags[i] = ((fptag & 3) == 3); + fptag >>= 2; + } + j = env->fpstt; + for(i = 0;i < 8; i++) { + memcpy(&env->fpregs[j].d, &fp->fpregs1[i * 10], 10); + j = (j + 1) & 7; + } + /* we must restore the default rounding state */ + fpuc = 0x037f | (env->fpuc & (3 << 10)); + asm volatile("fldcw %0" : : "m" (fpuc)); +} + +static void restore_native_fp_fxrstor(CPUState *env) +{ + struct fpxstate *fp = &fpx1; + int i, j, fptag; + + fp->fpuc = env->fpuc; + fp->fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + fptag = 0; + for(i = 0; i < 8; i++) + fptag |= (env->fptags[i] << i); + fp->fptag = fptag ^ 0xff; + + j = env->fpstt; + for(i = 0;i < 8; i++) { + memcpy(&fp->fpregs1[i * 16], &env->fpregs[j].d, 10); + j = (j + 1) & 7; + } + if (env->cpuid_features & CPUID_SSE) { + fp->mxcsr = env->mxcsr; + /* XXX: check if DAZ is not available */ + fp->mxcsr_mask = 0xffff; + memcpy(fp->xmm_regs, env->xmm_regs, CPU_NB_REGS * 16); + } + asm volatile ("fxrstor %0" : "=m" (*fp)); +} + +static void save_native_fp_fxsave(CPUState *env) +{ + struct fpxstate *fp = &fpx1; + int fptag, i, j; + uint16_t fpuc; + + asm volatile ("fxsave %0" : : "m" (*fp)); + env->fpuc = fp->fpuc; + env->fpstt = (fp->fpus >> 11) & 7; + env->fpus = fp->fpus & ~0x3800; + fptag = fp->fptag ^ 0xff; + for(i = 0;i < 8; i++) { + env->fptags[i] = (fptag >> i) & 1; + } + j = env->fpstt; + for(i = 0;i < 8; i++) { + memcpy(&env->fpregs[j].d, &fp->fpregs1[i * 16], 10); + j = (j + 1) & 7; + } + if (env->cpuid_features & CPUID_SSE) { + env->mxcsr = fp->mxcsr; + memcpy(env->xmm_regs, fp->xmm_regs, CPU_NB_REGS * 16); + } + + /* we must restore the default rounding state */ + asm volatile ("fninit"); + fpuc = 0x037f | (env->fpuc & (3 << 10)); + asm volatile("fldcw %0" : : "m" (fpuc)); +} + +static int do_syscall(CPUState *env, + struct kqemu_cpu_state *kenv) +{ + int selector; + + selector = (env->star >> 32) & 0xffff; +#ifdef __x86_64__ + if (env->hflags & HF_LMA_MASK) { + env->regs[R_ECX] = kenv->next_eip; + env->regs[11] = env->eflags; + + cpu_x86_set_cpl(env, 0); + cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK); + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_W_MASK | DESC_A_MASK); + env->eflags &= ~env->fmask; + if (env->hflags & HF_CS64_MASK) + env->eip = env->lstar; + else + env->eip = env->cstar; + } else +#endif + { + env->regs[R_ECX] = (uint32_t)kenv->next_eip; + + cpu_x86_set_cpl(env, 0); + cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_W_MASK | DESC_A_MASK); + env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK); + env->eip = (uint32_t)env->star; + } + return 2; +} + +#ifdef CONFIG_PROFILER + +#define PC_REC_SIZE 1 +#define PC_REC_HASH_BITS 16 +#define PC_REC_HASH_SIZE (1 << PC_REC_HASH_BITS) + +typedef struct PCRecord { + unsigned long pc; + int64_t count; + struct PCRecord *next; +} PCRecord; + +static PCRecord *pc_rec_hash[PC_REC_HASH_SIZE]; +static int nb_pc_records; + +static void kqemu_record_pc(unsigned long pc) +{ + unsigned long h; + PCRecord **pr, *r; + + h = pc / PC_REC_SIZE; + h = h ^ (h >> PC_REC_HASH_BITS); + h &= (PC_REC_HASH_SIZE - 1); + pr = &pc_rec_hash[h]; + for(;;) { + r = *pr; + if (r == NULL) + break; + if (r->pc == pc) { + r->count++; + return; + } + pr = &r->next; + } + r = malloc(sizeof(PCRecord)); + r->count = 1; + r->pc = pc; + r->next = NULL; + *pr = r; + nb_pc_records++; +} + +static int pc_rec_cmp(const void *p1, const void *p2) +{ + PCRecord *r1 = *(PCRecord **)p1; + PCRecord *r2 = *(PCRecord **)p2; + if (r1->count < r2->count) + return 1; + else if (r1->count == r2->count) + return 0; + else + return -1; +} + +static void kqemu_record_flush(void) +{ + PCRecord *r, *r_next; + int h; + + for(h = 0; h < PC_REC_HASH_SIZE; h++) { + for(r = pc_rec_hash[h]; r != NULL; r = r_next) { + r_next = r->next; + free(r); + } + pc_rec_hash[h] = NULL; + } + nb_pc_records = 0; +} + +void kqemu_record_dump(void) +{ + PCRecord **pr, *r; + int i, h; + FILE *f; + int64_t total, sum; + + pr = malloc(sizeof(PCRecord *) * nb_pc_records); + i = 0; + total = 0; + for(h = 0; h < PC_REC_HASH_SIZE; h++) { + for(r = pc_rec_hash[h]; r != NULL; r = r->next) { + pr[i++] = r; + total += r->count; + } + } + qsort(pr, nb_pc_records, sizeof(PCRecord *), pc_rec_cmp); + + f = fopen("/tmp/kqemu.stats", "w"); + if (!f) { + perror("/tmp/kqemu.stats"); + exit(1); + } + fprintf(f, "total: %lld\n", total); + sum = 0; + for(i = 0; i < nb_pc_records; i++) { + r = pr[i]; + sum += r->count; + fprintf(f, "%08lx: %lld %0.2f%% %0.2f%%\n", + r->pc, + r->count, + (double)r->count / (double)total * 100.0, + (double)sum / (double)total * 100.0); + } + fclose(f); + free(pr); + + kqemu_record_flush(); +} +#endif + +int kqemu_cpu_exec(CPUState *env) +{ + struct kqemu_cpu_state kcpu_state, *kenv = &kcpu_state; + int ret, cpl, i; +#ifdef CONFIG_PROFILER + int64_t ti; +#endif + +#ifdef _WIN32 + DWORD temp; +#endif + +#ifdef CONFIG_PROFILER + ti = profile_getclock(); +#endif +#ifdef DEBUG + if (loglevel & CPU_LOG_INT) { + fprintf(logfile, "kqemu: cpu_exec: enter\n"); + cpu_dump_state(env, logfile, fprintf, 0); + } +#endif + memcpy(kenv->regs, env->regs, sizeof(kenv->regs)); + kenv->eip = env->eip; + kenv->eflags = env->eflags; + memcpy(&kenv->segs, &env->segs, sizeof(env->segs)); + memcpy(&kenv->ldt, &env->ldt, sizeof(env->ldt)); + memcpy(&kenv->tr, &env->tr, sizeof(env->tr)); + memcpy(&kenv->gdt, &env->gdt, sizeof(env->gdt)); + memcpy(&kenv->idt, &env->idt, sizeof(env->idt)); + kenv->cr0 = env->cr[0]; + kenv->cr2 = env->cr[2]; + kenv->cr3 = env->cr[3]; + kenv->cr4 = env->cr[4]; + kenv->a20_mask = env->a20_mask; +#if KQEMU_VERSION >= 0x010100 + kenv->efer = env->efer; +#endif +#if KQEMU_VERSION >= 0x010300 + kenv->tsc_offset = 0; + kenv->star = env->star; + kenv->sysenter_cs = env->sysenter_cs; + kenv->sysenter_esp = env->sysenter_esp; + kenv->sysenter_eip = env->sysenter_eip; +#ifdef __x86_64__ + kenv->lstar = env->lstar; + kenv->cstar = env->cstar; + kenv->fmask = env->fmask; + kenv->kernelgsbase = env->kernelgsbase; +#endif +#endif + if (env->dr[7] & 0xff) { + kenv->dr7 = env->dr[7]; + kenv->dr0 = env->dr[0]; + kenv->dr1 = env->dr[1]; + kenv->dr2 = env->dr[2]; + kenv->dr3 = env->dr[3]; + } else { + kenv->dr7 = 0; + } + kenv->dr6 = env->dr[6]; + cpl = (env->hflags & HF_CPL_MASK); + kenv->cpl = cpl; + kenv->nb_pages_to_flush = nb_pages_to_flush; +#if KQEMU_VERSION >= 0x010200 + kenv->user_only = (env->kqemu_enabled == 1); + kenv->nb_ram_pages_to_update = nb_ram_pages_to_update; +#endif + nb_ram_pages_to_update = 0; + +#if KQEMU_VERSION >= 0x010300 + kenv->nb_modified_ram_pages = nb_modified_ram_pages; +#endif + kqemu_reset_modified_ram_pages(); + + if (env->cpuid_features & CPUID_FXSR) + restore_native_fp_fxrstor(env); + else + restore_native_fp_frstor(env); + +#ifdef _WIN32 + if (DeviceIoControl(kqemu_fd, KQEMU_EXEC, + kenv, sizeof(struct kqemu_cpu_state), + kenv, sizeof(struct kqemu_cpu_state), + &temp, NULL)) { + ret = kenv->retval; + } else { + ret = -1; + } +#else +#if KQEMU_VERSION >= 0x010100 + ioctl(kqemu_fd, KQEMU_EXEC, kenv); + ret = kenv->retval; +#else + ret = ioctl(kqemu_fd, KQEMU_EXEC, kenv); +#endif +#endif + if (env->cpuid_features & CPUID_FXSR) + save_native_fp_fxsave(env); + else + save_native_fp_fsave(env); + + memcpy(env->regs, kenv->regs, sizeof(env->regs)); + env->eip = kenv->eip; + env->eflags = kenv->eflags; + memcpy(env->segs, kenv->segs, sizeof(env->segs)); + cpu_x86_set_cpl(env, kenv->cpl); + memcpy(&env->ldt, &kenv->ldt, sizeof(env->ldt)); +#if 0 + /* no need to restore that */ + memcpy(env->tr, kenv->tr, sizeof(env->tr)); + memcpy(env->gdt, kenv->gdt, sizeof(env->gdt)); + memcpy(env->idt, kenv->idt, sizeof(env->idt)); + env->a20_mask = kenv->a20_mask; +#endif + env->cr[0] = kenv->cr0; + env->cr[4] = kenv->cr4; + env->cr[3] = kenv->cr3; + env->cr[2] = kenv->cr2; + env->dr[6] = kenv->dr6; +#if KQEMU_VERSION >= 0x010300 +#ifdef __x86_64__ + env->kernelgsbase = kenv->kernelgsbase; +#endif +#endif + + /* flush pages as indicated by kqemu */ + if (kenv->nb_pages_to_flush >= KQEMU_FLUSH_ALL) { + tlb_flush(env, 1); + } else { + for(i = 0; i < kenv->nb_pages_to_flush; i++) { + tlb_flush_page(env, pages_to_flush[i]); + } + } + nb_pages_to_flush = 0; + +#ifdef CONFIG_PROFILER + kqemu_time += profile_getclock() - ti; + kqemu_exec_count++; +#endif + +#if KQEMU_VERSION >= 0x010200 + if (kenv->nb_ram_pages_to_update > 0) { + cpu_tlb_update_dirty(env); + } +#endif + +#if KQEMU_VERSION >= 0x010300 + if (kenv->nb_modified_ram_pages > 0) { + for(i = 0; i < kenv->nb_modified_ram_pages; i++) { + unsigned long addr; + addr = modified_ram_pages[i]; + tb_invalidate_phys_page_range(addr, addr + TARGET_PAGE_SIZE, 0); + } + } +#endif + + /* restore the hidden flags */ + { + unsigned int new_hflags; +#ifdef TARGET_X86_64 + if ((env->hflags & HF_LMA_MASK) && + (env->segs[R_CS].flags & DESC_L_MASK)) { + /* long mode */ + new_hflags = HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK; + } else +#endif + { + /* legacy / compatibility case */ + new_hflags = (env->segs[R_CS].flags & DESC_B_MASK) + >> (DESC_B_SHIFT - HF_CS32_SHIFT); + new_hflags |= (env->segs[R_SS].flags & DESC_B_MASK) + >> (DESC_B_SHIFT - HF_SS32_SHIFT); + if (!(env->cr[0] & CR0_PE_MASK) || + (env->eflags & VM_MASK) || + !(env->hflags & HF_CS32_MASK)) { + /* XXX: try to avoid this test. The problem comes from the + fact that is real mode or vm86 mode we only modify the + 'base' and 'selector' fields of the segment cache to go + faster. A solution may be to force addseg to one in + translate-i386.c. */ + new_hflags |= HF_ADDSEG_MASK; + } else { + new_hflags |= ((env->segs[R_DS].base | + env->segs[R_ES].base | + env->segs[R_SS].base) != 0) << + HF_ADDSEG_SHIFT; + } + } + env->hflags = (env->hflags & + ~(HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK)) | + new_hflags; + } + /* update FPU flags */ + env->hflags = (env->hflags & ~(HF_MP_MASK | HF_EM_MASK | HF_TS_MASK)) | + ((env->cr[0] << (HF_MP_SHIFT - 1)) & (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK)); + if (env->cr[4] & CR4_OSFXSR_MASK) + env->hflags |= HF_OSFXSR_MASK; + else + env->hflags &= ~HF_OSFXSR_MASK; + +#ifdef DEBUG + if (loglevel & CPU_LOG_INT) { + fprintf(logfile, "kqemu: kqemu_cpu_exec: ret=0x%x\n", ret); + } +#endif + if (ret == KQEMU_RET_SYSCALL) { + /* syscall instruction */ + return do_syscall(env, kenv); + } else + if ((ret & 0xff00) == KQEMU_RET_INT) { + env->exception_index = ret & 0xff; + env->error_code = 0; + env->exception_is_int = 1; + env->exception_next_eip = kenv->next_eip; +#ifdef CONFIG_PROFILER + kqemu_ret_int_count++; +#endif +#ifdef DEBUG + if (loglevel & CPU_LOG_INT) { + fprintf(logfile, "kqemu: interrupt v=%02x:\n", + env->exception_index); + cpu_dump_state(env, logfile, fprintf, 0); + } +#endif + return 1; + } else if ((ret & 0xff00) == KQEMU_RET_EXCEPTION) { + env->exception_index = ret & 0xff; + env->error_code = kenv->error_code; + env->exception_is_int = 0; + env->exception_next_eip = 0; +#ifdef CONFIG_PROFILER + kqemu_ret_excp_count++; +#endif +#ifdef DEBUG + if (loglevel & CPU_LOG_INT) { + fprintf(logfile, "kqemu: exception v=%02x e=%04x:\n", + env->exception_index, env->error_code); + cpu_dump_state(env, logfile, fprintf, 0); + } +#endif + return 1; + } else if (ret == KQEMU_RET_INTR) { +#ifdef CONFIG_PROFILER + kqemu_ret_intr_count++; +#endif +#ifdef DEBUG + if (loglevel & CPU_LOG_INT) { + cpu_dump_state(env, logfile, fprintf, 0); + } +#endif + return 0; + } else if (ret == KQEMU_RET_SOFTMMU) { +#ifdef CONFIG_PROFILER + { + unsigned long pc = env->eip + env->segs[R_CS].base; + kqemu_record_pc(pc); + } +#endif +#ifdef DEBUG + if (loglevel & CPU_LOG_INT) { + cpu_dump_state(env, logfile, fprintf, 0); + } +#endif + return 2; + } else { + cpu_dump_state(env, stderr, fprintf, 0); + fprintf(stderr, "Unsupported return value: 0x%x\n", ret); + exit(1); + } + return 0; +} + +void kqemu_cpu_interrupt(CPUState *env) +{ +#if defined(_WIN32) && KQEMU_VERSION >= 0x010101 + /* cancelling the I/O request causes KQEMU to finish executing the + current block and successfully returning. */ + CancelIo(kqemu_fd); +#endif +} + +#endif diff --git a/tools/ioemu/kqemu.h b/tools/ioemu/kqemu.h new file mode 100644 index 0000000000..892e335935 --- /dev/null +++ b/tools/ioemu/kqemu.h @@ -0,0 +1,132 @@ +/* + * KQEMU header + * + * Copyright (c) 2004-2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef KQEMU_H +#define KQEMU_H + +#define KQEMU_VERSION 0x010300 + +struct kqemu_segment_cache { + uint32_t selector; + unsigned long base; + uint32_t limit; + uint32_t flags; +}; + +struct kqemu_cpu_state { +#ifdef __x86_64__ + unsigned long regs[16]; +#else + unsigned long regs[8]; +#endif + unsigned long eip; + unsigned long eflags; + + uint32_t dummy0, dummy1, dumm2, dummy3, dummy4; + + struct kqemu_segment_cache segs[6]; /* selector values */ + struct kqemu_segment_cache ldt; + struct kqemu_segment_cache tr; + struct kqemu_segment_cache gdt; /* only base and limit are used */ + struct kqemu_segment_cache idt; /* only base and limit are used */ + + unsigned long cr0; + unsigned long dummy5; + unsigned long cr2; + unsigned long cr3; + unsigned long cr4; + uint32_t a20_mask; + + /* sysenter registers */ + uint32_t sysenter_cs; + uint32_t sysenter_esp; + uint32_t sysenter_eip; + uint64_t efer __attribute__((aligned(8))); + uint64_t star; +#ifdef __x86_64__ + unsigned long lstar; + unsigned long cstar; + unsigned long fmask; + unsigned long kernelgsbase; +#endif + uint64_t tsc_offset; + + unsigned long dr0; + unsigned long dr1; + unsigned long dr2; + unsigned long dr3; + unsigned long dr6; + unsigned long dr7; + + uint8_t cpl; + uint8_t user_only; + + uint32_t error_code; /* error_code when exiting with an exception */ + unsigned long next_eip; /* next eip value when exiting with an interrupt */ + unsigned int nb_pages_to_flush; /* number of pages to flush, + KQEMU_FLUSH_ALL means full flush */ +#define KQEMU_MAX_PAGES_TO_FLUSH 512 +#define KQEMU_FLUSH_ALL (KQEMU_MAX_PAGES_TO_FLUSH + 1) + + long retval; + + /* number of ram_dirty entries to update */ + unsigned int nb_ram_pages_to_update; +#define KQEMU_MAX_RAM_PAGES_TO_UPDATE 512 +#define KQEMU_RAM_PAGES_UPDATE_ALL (KQEMU_MAX_RAM_PAGES_TO_UPDATE + 1) + +#define KQEMU_MAX_MODIFIED_RAM_PAGES 512 + unsigned int nb_modified_ram_pages; +}; + +struct kqemu_init { + uint8_t *ram_base; /* must be page aligned */ + unsigned long ram_size; /* must be multiple of 4 KB */ + uint8_t *ram_dirty; /* must be page aligned */ + uint32_t **phys_to_ram_map; /* must be page aligned */ + unsigned long *pages_to_flush; /* must be page aligned */ + unsigned long *ram_pages_to_update; /* must be page aligned */ + unsigned long *modified_ram_pages; /* must be page aligned */ +}; + +#define KQEMU_RET_ABORT (-1) +#define KQEMU_RET_EXCEPTION 0x0000 /* 8 low order bit are the exception */ +#define KQEMU_RET_INT 0x0100 /* 8 low order bit are the interrupt */ +#define KQEMU_RET_SOFTMMU 0x0200 /* emulation needed (I/O or + unsupported INSN) */ +#define KQEMU_RET_INTR 0x0201 /* interrupted by a signal */ +#define KQEMU_RET_SYSCALL 0x0300 /* syscall insn */ + +#ifdef _WIN32 +#define KQEMU_EXEC CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define KQEMU_INIT CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define KQEMU_GET_VERSION CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_READ_ACCESS) +#define KQEMU_MODIFY_RAM_PAGES CTL_CODE(FILE_DEVICE_UNKNOWN, 4, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#else +#define KQEMU_EXEC _IOWR('q', 1, struct kqemu_cpu_state) +#define KQEMU_INIT _IOW('q', 2, struct kqemu_init) +#define KQEMU_GET_VERSION _IOR('q', 3, int) +#define KQEMU_MODIFY_RAM_PAGES _IOW('q', 4, int) +#endif + +#endif /* KQEMU_H */ diff --git a/tools/ioemu/loader.c b/tools/ioemu/loader.c new file mode 100644 index 0000000000..b2d6423fed --- /dev/null +++ b/tools/ioemu/loader.c @@ -0,0 +1,235 @@ +/* + * QEMU Executable loader + * + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "disas.h" + +/* return the size or -1 if error */ +int get_image_size(const char *filename) +{ + int fd, size; + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) + return -1; + size = lseek(fd, 0, SEEK_END); + close(fd); + return size; +} + +/* return the size or -1 if error */ +int load_image(const char *filename, uint8_t *addr) +{ + int fd, size; + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) + return -1; + size = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + if (read(fd, addr, size) != size) { + close(fd); + return -1; + } + close(fd); + return size; +} + +/* A.OUT loader */ + +struct exec +{ + uint32_t a_info; /* Use macros N_MAGIC, etc for access */ + uint32_t a_text; /* length of text, in bytes */ + uint32_t a_data; /* length of data, in bytes */ + uint32_t a_bss; /* length of uninitialized data area, in bytes */ + uint32_t a_syms; /* length of symbol table data in file, in bytes */ + uint32_t a_entry; /* start address */ + uint32_t a_trsize; /* length of relocation info for text, in bytes */ + uint32_t a_drsize; /* length of relocation info for data, in bytes */ +}; + +#ifdef BSWAP_NEEDED +static void bswap_ahdr(struct exec *e) +{ + bswap32s(&e->a_info); + bswap32s(&e->a_text); + bswap32s(&e->a_data); + bswap32s(&e->a_bss); + bswap32s(&e->a_syms); + bswap32s(&e->a_entry); + bswap32s(&e->a_trsize); + bswap32s(&e->a_drsize); +} +#else +#define bswap_ahdr(x) do { } while (0) +#endif + +#define N_MAGIC(exec) ((exec).a_info & 0xffff) +#define OMAGIC 0407 +#define NMAGIC 0410 +#define ZMAGIC 0413 +#define QMAGIC 0314 +#define _N_HDROFF(x) (1024 - sizeof (struct exec)) +#define N_TXTOFF(x) \ + (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \ + (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec))) +#define N_TXTADDR(x) (N_MAGIC(x) == QMAGIC ? TARGET_PAGE_SIZE : 0) +#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text) +#define _N_SEGMENT_ROUND(x) (((x) + TARGET_PAGE_SIZE - 1) & ~(TARGET_PAGE_SIZE - 1)) + +#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text) + +#define N_DATADDR(x) \ + (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \ + : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x)))) + + +int load_aout(const char *filename, uint8_t *addr) +{ + int fd, size, ret; + struct exec e; + uint32_t magic; + + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) + return -1; + + size = read(fd, &e, sizeof(e)); + if (size < 0) + goto fail; + + bswap_ahdr(&e); + + magic = N_MAGIC(e); + switch (magic) { + case ZMAGIC: + case QMAGIC: + case OMAGIC: + lseek(fd, N_TXTOFF(e), SEEK_SET); + size = read(fd, addr, e.a_text + e.a_data); + if (size < 0) + goto fail; + break; + case NMAGIC: + lseek(fd, N_TXTOFF(e), SEEK_SET); + size = read(fd, addr, e.a_text); + if (size < 0) + goto fail; + ret = read(fd, addr + N_DATADDR(e), e.a_data); + if (ret < 0) + goto fail; + size += ret; + break; + default: + goto fail; + } + close(fd); + return size; + fail: + close(fd); + return -1; +} + +/* ELF loader */ + +static void *load_at(int fd, int offset, int size) +{ + void *ptr; + if (lseek(fd, offset, SEEK_SET) < 0) + return NULL; + ptr = qemu_malloc(size); + if (!ptr) + return NULL; + if (read(fd, ptr, size) != size) { + qemu_free(ptr); + return NULL; + } + return ptr; +} + + +#define ELF_CLASS ELFCLASS32 +#include "elf.h" + +#define SZ 32 +#define elf_word uint32_t +#define bswapSZs bswap32s +#include "elf_ops.h" + +#undef elfhdr +#undef elf_phdr +#undef elf_shdr +#undef elf_sym +#undef elf_note +#undef elf_word +#undef bswapSZs +#undef SZ +#define elfhdr elf64_hdr +#define elf_phdr elf64_phdr +#define elf_note elf64_note +#define elf_shdr elf64_shdr +#define elf_sym elf64_sym +#define elf_word uint64_t +#define bswapSZs bswap64s +#define SZ 64 +#include "elf_ops.h" + +/* return < 0 if error, otherwise the number of bytes loaded in memory */ +int load_elf(const char *filename, int64_t virt_to_phys_addend, + uint64_t *pentry) +{ + int fd, data_order, must_swab, ret; + uint8_t e_ident[EI_NIDENT]; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror(filename); + return -1; + } + if (read(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident)) + goto fail; + if (e_ident[0] != ELFMAG0 || + e_ident[1] != ELFMAG1 || + e_ident[2] != ELFMAG2 || + e_ident[3] != ELFMAG3) + goto fail; +#ifdef WORDS_BIGENDIAN + data_order = ELFDATA2MSB; +#else + data_order = ELFDATA2LSB; +#endif + must_swab = data_order != e_ident[EI_DATA]; + + lseek(fd, 0, SEEK_SET); + if (e_ident[EI_CLASS] == ELFCLASS64) { + ret = load_elf64(fd, virt_to_phys_addend, must_swab, pentry); + } else { + ret = load_elf32(fd, virt_to_phys_addend, must_swab, pentry); + } + + close(fd); + return ret; + + fail: + close(fd); + return -1; +} diff --git a/tools/ioemu/monitor.c b/tools/ioemu/monitor.c new file mode 100644 index 0000000000..e5d49d7192 --- /dev/null +++ b/tools/ioemu/monitor.c @@ -0,0 +1,2335 @@ +/* + * QEMU monitor + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" +#include "disas.h" +#include + +//#define DEBUG +//#define DEBUG_COMPLETION + +#ifndef offsetof +#define offsetof(type, field) ((size_t) &((type *)0)->field) +#endif + +/* + * Supported types: + * + * 'F' filename + * 'B' block device name + * 's' string (accept optional quote) + * 'i' 32 bit integer + * 'l' target long (32 or 64 bit) + * '/' optional gdb-like print format (like "/10x") + * + * '?' optional type (for 'F', 's' and 'i') + * + */ + +typedef struct term_cmd_t { + const char *name; + const char *args_type; + void (*handler)(); + const char *params; + const char *help; +} term_cmd_t; + +static CharDriverState *monitor_hd; + +static term_cmd_t term_cmds[]; +static term_cmd_t info_cmds[]; + +static char term_outbuf[1024]; +static int term_outbuf_index; + +static void monitor_start_input(void); + +CPUState *mon_cpu = NULL; + +void term_flush(void) +{ +#ifdef CONFIG_DM + if (term_outbuf_index > 0 && !monitor_hd) { + fwrite(term_outbuf, term_outbuf_index, 1, stderr); + term_outbuf_index = 0; + } +#endif + if (term_outbuf_index > 0) { + qemu_chr_write(monitor_hd, term_outbuf, term_outbuf_index); + term_outbuf_index = 0; + } +} + +/* flush at every end of line or if the buffer is full */ +void term_puts(const char *str) +{ + int c; + for(;;) { + c = *str++; + if (c == '\0') + break; + term_outbuf[term_outbuf_index++] = c; + if (term_outbuf_index >= sizeof(term_outbuf) || + c == '\n') + term_flush(); + } +} + +void term_vprintf(const char *fmt, va_list ap) +{ + char buf[4096]; + vsnprintf(buf, sizeof(buf), fmt, ap); + term_puts(buf); +} + +void term_printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + term_vprintf(fmt, ap); + va_end(ap); +} + +#ifndef CONFIG_DM +static int monitor_fprintf(FILE *stream, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + term_vprintf(fmt, ap); + va_end(ap); + return 0; +} +#endif /* !CONFIG_DM */ + +static int compare_cmd(const char *name, const char *list) +{ + const char *p, *pstart; + int len; + len = strlen(name); + p = list; + for(;;) { + pstart = p; + p = strchr(p, '|'); + if (!p) + p = pstart + strlen(pstart); + if ((p - pstart) == len && !memcmp(pstart, name, len)) + return 1; + if (*p == '\0') + break; + p++; + } + return 0; +} + +static void help_cmd1(term_cmd_t *cmds, const char *prefix, const char *name) +{ + term_cmd_t *cmd; + + for(cmd = cmds; cmd->name != NULL; cmd++) { + if (!name || !strcmp(name, cmd->name)) + term_printf("%s%s %s -- %s\n", prefix, cmd->name, cmd->params, cmd->help); + } +} + +static void help_cmd(const char *name) +{ + if (name && !strcmp(name, "info")) { + help_cmd1(info_cmds, "info ", NULL); + } else { + help_cmd1(term_cmds, "", name); + if (name && !strcmp(name, "log")) { + CPULogItem *item; + term_printf("Log items (comma separated):\n"); + term_printf("%-10s %s\n", "none", "remove all logs"); + for(item = cpu_log_items; item->mask != 0; item++) { + term_printf("%-10s %s\n", item->name, item->help); + } + } + } +} + +static void do_help(const char *name) +{ + help_cmd(name); +} + +static void do_commit(void) +{ + int i; + + for (i = 0; i < MAX_DISKS; i++) { + if (bs_table[i]) { + bdrv_commit(bs_table[i]); + } + } +} + +static void do_info(const char *item) +{ + term_cmd_t *cmd; + + if (!item) + goto help; + for(cmd = info_cmds; cmd->name != NULL; cmd++) { + if (compare_cmd(item, cmd->name)) + goto found; + } + help: + help_cmd("info"); + return; + found: + cmd->handler(); +} + +static void do_info_version(void) +{ + term_printf("%s\n", QEMU_VERSION); +} + +static void do_info_block(void) +{ + bdrv_info(); +} + +/* get the current CPU defined by the user */ +int mon_set_cpu(int cpu_index) +{ + CPUState *env; + + for(env = first_cpu; env != NULL; env = env->next_cpu) { + if (env->cpu_index == cpu_index) { + mon_cpu = env; + return 0; + } + } + return -1; +} + +CPUState *mon_get_cpu(void) +{ + if (!mon_cpu) { + mon_set_cpu(0); + } + return mon_cpu; +} + +#ifndef CONFIG_DM +static void do_info_registers(void) +{ + CPUState *env; + env = mon_get_cpu(); + if (!env) + return; +#ifdef TARGET_I386 + cpu_dump_state(env, NULL, monitor_fprintf, + X86_DUMP_FPU); +#else + cpu_dump_state(env, NULL, monitor_fprintf, + 0); +#endif +} + +static void do_info_cpus(void) +{ + CPUState *env; + + /* just to set the default cpu if not already done */ + mon_get_cpu(); + + for(env = first_cpu; env != NULL; env = env->next_cpu) { + term_printf("%c CPU #%d:", + (env == mon_cpu) ? '*' : ' ', + env->cpu_index); +#if defined(TARGET_I386) + term_printf(" pc=0x" TARGET_FMT_lx, env->eip + env->segs[R_CS].base); + if (env->hflags & HF_HALTED_MASK) + term_printf(" (halted)"); +#elif defined(TARGET_PPC) + term_printf(" nip=0x" TARGET_FMT_lx, env->nip); + if (env->halted) + term_printf(" (halted)"); +#elif defined(TARGET_SPARC) + term_printf(" pc=0x" TARGET_FMT_lx " npc=0x" TARGET_FMT_lx, env->pc, env->npc); + if (env->halted) + term_printf(" (halted)"); +#endif + term_printf("\n"); + } +} + +static void do_cpu_set(int index) +{ + if (mon_set_cpu(index) < 0) + term_printf("Invalid CPU index\n"); +} + +static void do_info_jit(void) +{ + dump_exec_info(NULL, monitor_fprintf); +} +#endif /* !CONFIG_DM */ + +static void do_info_history (void) +{ + int i; + const char *str; + + i = 0; + for(;;) { + str = readline_get_history(i); + if (!str) + break; + term_printf("%d: '%s'\n", i, str); + i++; + } +} + +static void do_quit(void) +{ + destroy_hvm_domain(); + exit(0); +} + +static int eject_device(BlockDriverState *bs, int force) +{ + if (bdrv_is_inserted(bs)) { + if (!force) { + if (!bdrv_is_removable(bs)) { + term_printf("device is not removable\n"); + return -1; + } + if (bdrv_is_locked(bs)) { + term_printf("device is locked\n"); + return -1; + } + } + bdrv_close(bs); + } + return 0; +} + +static void do_eject(int force, const char *filename) +{ + BlockDriverState *bs; + + bs = bdrv_find(filename); + if (!bs) { + term_printf("device not found\n"); + return; + } + eject_device(bs, force); +} + +static void do_change(const char *device, const char *filename) +{ + BlockDriverState *bs; + int i; + char password[256]; + + bs = bdrv_find(device); + if (!bs) { + term_printf("device not found\n"); + return; + } + if (eject_device(bs, 0) < 0) + return; + bdrv_open(bs, filename, 0); + if (bdrv_is_encrypted(bs)) { + term_printf("%s is encrypted.\n", device); + for(i = 0; i < 3; i++) { + monitor_readline("Password: ", 1, password, sizeof(password)); + if (bdrv_set_key(bs, password) == 0) + break; + term_printf("invalid password\n"); + } + } +} + +static void do_screen_dump(const char *filename) +{ + vga_hw_screen_dump(filename); +} + +static void do_log(const char *items) +{ + int mask; + + if (!strcmp(items, "none")) { + mask = 0; + } else { + mask = cpu_str_to_log_mask(items); + if (!mask) { + help_cmd("log"); + return; + } + } + cpu_set_log(mask); +} + +#ifndef CONFIG_DM +static void do_savevm(const char *filename) +{ + if (qemu_savevm(filename) < 0) + term_printf("I/O error when saving VM to '%s'\n", filename); +} + +static void do_loadvm(const char *filename) +{ + if (qemu_loadvm(filename) < 0) + term_printf("I/O error when loading VM from '%s'\n", filename); +} + +static void do_stop(void) +{ + vm_stop(EXCP_INTERRUPT); +} + +static void do_cont(void) +{ + vm_start(); +} + +#ifdef CONFIG_GDBSTUB +static void do_gdbserver(int has_port, int port) +{ + if (!has_port) + port = DEFAULT_GDBSTUB_PORT; + if (gdbserver_start(port) < 0) { + qemu_printf("Could not open gdbserver socket on port %d\n", port); + } else { + qemu_printf("Waiting gdb connection on port %d\n", port); + } +} +#endif + +static void term_printc(int c) +{ + term_printf("'"); + switch(c) { + case '\'': + term_printf("\\'"); + break; + case '\\': + term_printf("\\\\"); + break; + case '\n': + term_printf("\\n"); + break; + case '\r': + term_printf("\\r"); + break; + default: + if (c >= 32 && c <= 126) { + term_printf("%c", c); + } else { + term_printf("\\x%02x", c); + } + break; + } + term_printf("'"); +} + +static void memory_dump(int count, int format, int wsize, + target_ulong addr, int is_physical) +{ + CPUState *env; + int nb_per_line, l, line_size, i, max_digits, len; + uint8_t buf[16]; + uint64_t v; + + if (format == 'i') { + int flags; + flags = 0; + env = mon_get_cpu(); + if (!env && !is_physical) + return; +#ifdef TARGET_I386 + if (wsize == 2) { + flags = 1; + } else if (wsize == 4) { + flags = 0; + } else { + /* as default we use the current CS size */ + flags = 0; + if (env) { +#ifdef TARGET_X86_64 + if ((env->efer & MSR_EFER_LMA) && + (env->segs[R_CS].flags & DESC_L_MASK)) + flags = 2; + else +#endif + if (!(env->segs[R_CS].flags & DESC_B_MASK)) + flags = 1; + } + } +#endif + monitor_disas(env, addr, count, is_physical, flags); + return; + } + + len = wsize * count; + if (wsize == 1) + line_size = 8; + else + line_size = 16; + nb_per_line = line_size / wsize; + max_digits = 0; + + switch(format) { + case 'o': + max_digits = (wsize * 8 + 2) / 3; + break; + default: + case 'x': + max_digits = (wsize * 8) / 4; + break; + case 'u': + case 'd': + max_digits = (wsize * 8 * 10 + 32) / 33; + break; + case 'c': + wsize = 1; + break; + } + + while (len > 0) { + term_printf(TARGET_FMT_lx ":", addr); + l = len; + if (l > line_size) + l = line_size; + if (is_physical) { + cpu_physical_memory_rw(addr, buf, l, 0); + } else { + env = mon_get_cpu(); + if (!env) + break; + cpu_memory_rw_debug(env, addr, buf, l, 0); + } + i = 0; + while (i < l) { + switch(wsize) { + default: + case 1: + v = ldub_raw(buf + i); + break; + case 2: + v = lduw_raw(buf + i); + break; + case 4: + v = (uint32_t)ldl_raw(buf + i); + break; + case 8: + v = ldq_raw(buf + i); + break; + } + term_printf(" "); + switch(format) { + case 'o': + term_printf("%#*llo", max_digits, v); + break; + case 'x': + term_printf("0x%0*llx", max_digits, v); + break; + case 'u': + term_printf("%*llu", max_digits, v); + break; + case 'd': + term_printf("%*lld", max_digits, v); + break; + case 'c': + term_printc(v); + break; + } + i += wsize; + } + term_printf("\n"); + addr += l; + len -= l; + } +} + +#if TARGET_LONG_BITS == 64 +#define GET_TLONG(h, l) (((uint64_t)(h) << 32) | (l)) +#else +#define GET_TLONG(h, l) (l) +#endif + +static void do_memory_dump(int count, int format, int size, + uint32_t addrh, uint32_t addrl) +{ + target_long addr = GET_TLONG(addrh, addrl); + memory_dump(count, format, size, addr, 0); +} + +static void do_physical_memory_dump(int count, int format, int size, + uint32_t addrh, uint32_t addrl) + +{ + target_long addr = GET_TLONG(addrh, addrl); + memory_dump(count, format, size, addr, 1); +} + +static void do_print(int count, int format, int size, unsigned int valh, unsigned int vall) +{ + target_long val = GET_TLONG(valh, vall); +#if TARGET_LONG_BITS == 32 + switch(format) { + case 'o': + term_printf("%#o", val); + break; + case 'x': + term_printf("%#x", val); + break; + case 'u': + term_printf("%u", val); + break; + default: + case 'd': + term_printf("%d", val); + break; + case 'c': + term_printc(val); + break; + } +#else + switch(format) { + case 'o': + term_printf("%#llo", val); + break; + case 'x': + term_printf("%#llx", val); + break; + case 'u': + term_printf("%llu", val); + break; + default: + case 'd': + term_printf("%lld", val); + break; + case 'c': + term_printc(val); + break; + } +#endif + term_printf("\n"); +} +#endif /* !CONFIG_DM */ + +static void do_sum(uint32_t start, uint32_t size) +{ + uint32_t addr; + uint8_t buf[1]; + uint16_t sum; + + sum = 0; + for(addr = start; addr < (start + size); addr++) { + cpu_physical_memory_rw(addr, buf, 1, 0); + /* BSD sum algorithm ('sum' Unix command) */ + sum = (sum >> 1) | (sum << 15); + sum += buf[0]; + } + term_printf("%05d\n", sum); +} + +typedef struct { + int keycode; + const char *name; +} KeyDef; + +static const KeyDef key_defs[] = { + { 0x2a, "shift" }, + { 0x36, "shift_r" }, + + { 0x38, "alt" }, + { 0xb8, "alt_r" }, + { 0x1d, "ctrl" }, + { 0x9d, "ctrl_r" }, + + { 0xdd, "menu" }, + + { 0x01, "esc" }, + + { 0x02, "1" }, + { 0x03, "2" }, + { 0x04, "3" }, + { 0x05, "4" }, + { 0x06, "5" }, + { 0x07, "6" }, + { 0x08, "7" }, + { 0x09, "8" }, + { 0x0a, "9" }, + { 0x0b, "0" }, + { 0x0e, "backspace" }, + + { 0x0f, "tab" }, + { 0x10, "q" }, + { 0x11, "w" }, + { 0x12, "e" }, + { 0x13, "r" }, + { 0x14, "t" }, + { 0x15, "y" }, + { 0x16, "u" }, + { 0x17, "i" }, + { 0x18, "o" }, + { 0x19, "p" }, + + { 0x1c, "ret" }, + + { 0x1e, "a" }, + { 0x1f, "s" }, + { 0x20, "d" }, + { 0x21, "f" }, + { 0x22, "g" }, + { 0x23, "h" }, + { 0x24, "j" }, + { 0x25, "k" }, + { 0x26, "l" }, + + { 0x2c, "z" }, + { 0x2d, "x" }, + { 0x2e, "c" }, + { 0x2f, "v" }, + { 0x30, "b" }, + { 0x31, "n" }, + { 0x32, "m" }, + + { 0x39, "spc" }, + { 0x3a, "caps_lock" }, + { 0x3b, "f1" }, + { 0x3c, "f2" }, + { 0x3d, "f3" }, + { 0x3e, "f4" }, + { 0x3f, "f5" }, + { 0x40, "f6" }, + { 0x41, "f7" }, + { 0x42, "f8" }, + { 0x43, "f9" }, + { 0x44, "f10" }, + { 0x45, "num_lock" }, + { 0x46, "scroll_lock" }, + + { 0x56, "<" }, + + { 0x57, "f11" }, + { 0x58, "f12" }, + + { 0xb7, "print" }, + + { 0xc7, "home" }, + { 0xc9, "pgup" }, + { 0xd1, "pgdn" }, + { 0xcf, "end" }, + + { 0xcb, "left" }, + { 0xc8, "up" }, + { 0xd0, "down" }, + { 0xcd, "right" }, + + { 0xd2, "insert" }, + { 0xd3, "delete" }, + { 0, NULL }, +}; + +static int get_keycode(const char *key) +{ + const KeyDef *p; + + for(p = key_defs; p->name != NULL; p++) { + if (!strcmp(key, p->name)) + return p->keycode; + } + return -1; +} + +static void do_send_key(const char *string) +{ + char keybuf[16], *q; + uint8_t keycodes[16]; + const char *p; + int nb_keycodes, keycode, i; + + nb_keycodes = 0; + p = string; + while (*p != '\0') { + q = keybuf; + while (*p != '\0' && *p != '-') { + if ((q - keybuf) < sizeof(keybuf) - 1) { + *q++ = *p; + } + p++; + } + *q = '\0'; + keycode = get_keycode(keybuf); + if (keycode < 0) { + term_printf("unknown key: '%s'\n", keybuf); + return; + } + keycodes[nb_keycodes++] = keycode; + if (*p == '\0') + break; + p++; + } + /* key down events */ + for(i = 0; i < nb_keycodes; i++) { + keycode = keycodes[i]; + if (keycode & 0x80) + kbd_put_keycode(0xe0); + kbd_put_keycode(keycode & 0x7f); + } + /* key up events */ + for(i = nb_keycodes - 1; i >= 0; i--) { + keycode = keycodes[i]; + if (keycode & 0x80) + kbd_put_keycode(0xe0); + kbd_put_keycode(keycode | 0x80); + } +} + +#ifndef CONFIG_DM +static void do_ioport_read(int count, int format, int size, int addr, int has_index, int index) +{ + uint32_t val; + int suffix; + + if (has_index) { + cpu_outb(NULL, addr & 0xffff, index & 0xff); + addr++; + } + addr &= 0xffff; + + switch(size) { + default: + case 1: + val = cpu_inb(NULL, addr); + suffix = 'b'; + break; + case 2: + val = cpu_inw(NULL, addr); + suffix = 'w'; + break; + case 4: + val = cpu_inl(NULL, addr); + suffix = 'l'; + break; + } + term_printf("port%c[0x%04x] = %#0*x\n", + suffix, addr, size * 2, val); +} + +static void do_system_reset(void) +{ + qemu_system_reset_request(); +} + +static void do_system_powerdown(void) +{ + qemu_system_powerdown_request(); +} + +#if defined(TARGET_I386) +static void print_pte(uint32_t addr, uint32_t pte, uint32_t mask) +{ + term_printf("%08x: %08x %c%c%c%c%c%c%c%c\n", + addr, + pte & mask, + pte & PG_GLOBAL_MASK ? 'G' : '-', + pte & PG_PSE_MASK ? 'P' : '-', + pte & PG_DIRTY_MASK ? 'D' : '-', + pte & PG_ACCESSED_MASK ? 'A' : '-', + pte & PG_PCD_MASK ? 'C' : '-', + pte & PG_PWT_MASK ? 'T' : '-', + pte & PG_USER_MASK ? 'U' : '-', + pte & PG_RW_MASK ? 'W' : '-'); +} + +static void tlb_info(void) +{ + CPUState *env; + int l1, l2; + uint32_t pgd, pde, pte; + + env = mon_get_cpu(); + if (!env) + return; + + if (!(env->cr[0] & CR0_PG_MASK)) { + term_printf("PG disabled\n"); + return; + } + pgd = env->cr[3] & ~0xfff; + for(l1 = 0; l1 < 1024; l1++) { + cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4); + pde = le32_to_cpu(pde); + if (pde & PG_PRESENT_MASK) { + if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { + print_pte((l1 << 22), pde, ~((1 << 20) - 1)); + } else { + for(l2 = 0; l2 < 1024; l2++) { + cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, + (uint8_t *)&pte, 4); + pte = le32_to_cpu(pte); + if (pte & PG_PRESENT_MASK) { + print_pte((l1 << 22) + (l2 << 12), + pte & ~PG_PSE_MASK, + ~0xfff); + } + } + } + } + } +} + +static void mem_print(uint32_t *pstart, int *plast_prot, + uint32_t end, int prot) +{ + int prot1; + prot1 = *plast_prot; + if (prot != prot1) { + if (*pstart != -1) { + term_printf("%08x-%08x %08x %c%c%c\n", + *pstart, end, end - *pstart, + prot1 & PG_USER_MASK ? 'u' : '-', + 'r', + prot1 & PG_RW_MASK ? 'w' : '-'); + } + if (prot != 0) + *pstart = end; + else + *pstart = -1; + *plast_prot = prot; + } +} + +static void mem_info(void) +{ + CPUState *env; + int l1, l2, prot, last_prot; + uint32_t pgd, pde, pte, start, end; + + env = mon_get_cpu(); + if (!env) + return; + + if (!(env->cr[0] & CR0_PG_MASK)) { + term_printf("PG disabled\n"); + return; + } + pgd = env->cr[3] & ~0xfff; + last_prot = 0; + start = -1; + for(l1 = 0; l1 < 1024; l1++) { + cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4); + pde = le32_to_cpu(pde); + end = l1 << 22; + if (pde & PG_PRESENT_MASK) { + if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { + prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); + mem_print(&start, &last_prot, end, prot); + } else { + for(l2 = 0; l2 < 1024; l2++) { + cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, + (uint8_t *)&pte, 4); + pte = le32_to_cpu(pte); + end = (l1 << 22) + (l2 << 12); + if (pte & PG_PRESENT_MASK) { + prot = pte & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); + } else { + prot = 0; + } + mem_print(&start, &last_prot, end, prot); + } + } + } else { + prot = 0; + mem_print(&start, &last_prot, end, prot); + } + } +} +#endif +#endif /* !CONFIG_DM */ + +static void do_info_kqemu(void) +{ +#ifdef USE_KQEMU + CPUState *env; + int val; + val = 0; + env = mon_get_cpu(); + if (!env) { + term_printf("No cpu initialized yet"); + return; + } + val = env->kqemu_enabled; + term_printf("kqemu support: "); + switch(val) { + default: + case 0: + term_printf("disabled\n"); + break; + case 1: + term_printf("enabled for user code\n"); + break; + case 2: + term_printf("enabled for user and kernel code\n"); + break; + } +#else + term_printf("kqemu support: not compiled\n"); +#endif +} + +#ifdef CONFIG_PROFILER + +int64_t kqemu_time; +int64_t qemu_time; +int64_t kqemu_exec_count; +int64_t dev_time; +int64_t kqemu_ret_int_count; +int64_t kqemu_ret_excp_count; +int64_t kqemu_ret_intr_count; + +static void do_info_profile(void) +{ + int64_t total; + total = qemu_time; + if (total == 0) + total = 1; + term_printf("async time %lld (%0.3f)\n", + dev_time, dev_time / (double)ticks_per_sec); + term_printf("qemu time %lld (%0.3f)\n", + qemu_time, qemu_time / (double)ticks_per_sec); + term_printf("kqemu time %lld (%0.3f %0.1f%%) count=%lld int=%lld excp=%lld intr=%lld\n", + kqemu_time, kqemu_time / (double)ticks_per_sec, + kqemu_time / (double)total * 100.0, + kqemu_exec_count, + kqemu_ret_int_count, + kqemu_ret_excp_count, + kqemu_ret_intr_count); + qemu_time = 0; + kqemu_time = 0; + kqemu_exec_count = 0; + dev_time = 0; + kqemu_ret_int_count = 0; + kqemu_ret_excp_count = 0; + kqemu_ret_intr_count = 0; +#ifdef USE_KQEMU + kqemu_record_dump(); +#endif +} +#else +static void do_info_profile(void) +{ + term_printf("Internal profiler not compiled\n"); +} +#endif + +static term_cmd_t term_cmds[] = { + { "help|?", "s?", do_help, + "[cmd]", "show the help" }, + { "commit", "", do_commit, + "", "commit changes to the disk images (if -snapshot is used)" }, + { "info", "s?", do_info, + "subcommand", "show various information about the system state" }, + { "q|quit", "", do_quit, + "", "quit the emulator" }, + { "eject", "-fB", do_eject, + "[-f] device", "eject a removable media (use -f to force it)" }, + { "change", "BF", do_change, + "device filename", "change a removable media" }, + { "screendump", "F", do_screen_dump, + "filename", "save screen into PPM image 'filename'" }, + { "log", "s", do_log, + "item1[,...]", "activate logging of the specified items to '/tmp/qemu.log'" }, +#ifndef CONFIG_DM + { "savevm", "F", do_savevm, + "filename", "save the whole virtual machine state to 'filename'" }, + { "loadvm", "F", do_loadvm, + "filename", "restore the whole virtual machine state from 'filename'" }, + { "stop", "", do_stop, + "", "stop emulation", }, + { "c|cont", "", do_cont, + "", "resume emulation", }, +#ifdef CONFIG_GDBSTUB + { "gdbserver", "i?", do_gdbserver, + "[port]", "start gdbserver session (default port=1234)", }, +#endif + { "x", "/l", do_memory_dump, + "/fmt addr", "virtual memory dump starting at 'addr'", }, + { "xp", "/l", do_physical_memory_dump, + "/fmt addr", "physical memory dump starting at 'addr'", }, + { "p|print", "/l", do_print, + "/fmt expr", "print expression value (use $reg for CPU register access)", }, + { "i", "/ii.", do_ioport_read, + "/fmt addr", "I/O port read" }, +#endif/* !CONFIG_DM */ + + { "sendkey", "s", do_send_key, + "keys", "send keys to the VM (e.g. 'sendkey ctrl-alt-f1')" }, +#ifndef CONFIG_DM + { "system_reset", "", do_system_reset, + "", "reset the system" }, + { "system_powerdown", "", do_system_powerdown, + "", "send system power down event" }, +#endif /* !CONFIG_DM */ + { "sum", "ii", do_sum, + "addr size", "compute the checksum of a memory region" }, + { "usb_add", "s", do_usb_add, + "device", "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')" }, + { "usb_del", "s", do_usb_del, + "device", "remove USB device 'bus.addr'" }, +#ifndef CONFIG_DM + { "cpu", "i", do_cpu_set, + "index", "set the default CPU" }, +#endif /* !CONFIG_DM */ + { NULL, NULL, }, +}; + +static term_cmd_t info_cmds[] = { + { "version", "", do_info_version, + "", "show the version of qemu" }, + { "network", "", do_info_network, + "", "show the network state" }, + { "block", "", do_info_block, + "", "show the block devices" }, +#ifndef CONFIG_DM + { "registers", "", do_info_registers, + "", "show the cpu registers" }, + { "cpus", "", do_info_cpus, + "", "show infos for each CPU" }, +#endif /* !CONFIG_DM */ + { "history", "", do_info_history, + "", "show the command line history", }, + { "irq", "", irq_info, + "", "show the interrupts statistics (if available)", }, + { "pic", "", pic_info, + "", "show i8259 (PIC) state", }, + { "pci", "", pci_info, + "", "show PCI info", }, +#ifndef CONFIG_DM +#if defined(TARGET_I386) + { "tlb", "", tlb_info, + "", "show virtual to physical memory mappings", }, + { "mem", "", mem_info, + "", "show the active virtual memory mappings", }, +#endif + { "jit", "", do_info_jit, + "", "show dynamic compiler info", }, +#endif /* !CONFIG_DM */ + { "kqemu", "", do_info_kqemu, + "", "show kqemu information", }, + { "usb", "", usb_info, + "", "show guest USB devices", }, + { "usbhost", "", usb_host_info, + "", "show host USB devices", }, + { "profile", "", do_info_profile, + "", "show profiling information", }, +#ifdef CONFIG_DM + { "hvmiopage", "", sp_info, + "", "show HVM device model shared page info", }, +#endif /* CONFIG_DM */ + { NULL, NULL, }, +}; + +/*******************************************************************/ + +#ifndef CONFIG_DM +static const char *pch; +static jmp_buf expr_env; + +#define MD_TLONG 0 +#define MD_I32 1 + +typedef struct MonitorDef { + const char *name; + int offset; + target_long (*get_value)(struct MonitorDef *md, int val); + int type; +} MonitorDef; + +#if defined(TARGET_I386) +static target_long monitor_get_pc (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return env->eip + env->segs[R_CS].base; +} +#endif + +#if defined(TARGET_PPC) +static target_long monitor_get_ccr (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + unsigned int u; + int i; + + if (!env) + return 0; + + u = 0; + for (i = 0; i < 8; i++) + u |= env->crf[i] << (32 - (4 * i)); + + return u; +} + +static target_long monitor_get_msr (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return (env->msr[MSR_POW] << MSR_POW) | + (env->msr[MSR_ILE] << MSR_ILE) | + (env->msr[MSR_EE] << MSR_EE) | + (env->msr[MSR_PR] << MSR_PR) | + (env->msr[MSR_FP] << MSR_FP) | + (env->msr[MSR_ME] << MSR_ME) | + (env->msr[MSR_FE0] << MSR_FE0) | + (env->msr[MSR_SE] << MSR_SE) | + (env->msr[MSR_BE] << MSR_BE) | + (env->msr[MSR_FE1] << MSR_FE1) | + (env->msr[MSR_IP] << MSR_IP) | + (env->msr[MSR_IR] << MSR_IR) | + (env->msr[MSR_DR] << MSR_DR) | + (env->msr[MSR_RI] << MSR_RI) | + (env->msr[MSR_LE] << MSR_LE); +} + +static target_long monitor_get_xer (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return (env->xer[XER_SO] << XER_SO) | + (env->xer[XER_OV] << XER_OV) | + (env->xer[XER_CA] << XER_CA) | + (env->xer[XER_BC] << XER_BC); +} + +static target_long monitor_get_decr (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return cpu_ppc_load_decr(env); +} + +static target_long monitor_get_tbu (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return cpu_ppc_load_tbu(env); +} + +static target_long monitor_get_tbl (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return cpu_ppc_load_tbl(env); +} +#endif + +#if defined(TARGET_SPARC) +#ifndef TARGET_SPARC64 +static target_long monitor_get_psr (struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return GET_PSR(env); +} +#endif + +static target_long monitor_get_reg(struct MonitorDef *md, int val) +{ + CPUState *env = mon_get_cpu(); + if (!env) + return 0; + return env->regwptr[val]; +} +#endif + +static MonitorDef monitor_defs[] = { +#ifdef TARGET_I386 + +#define SEG(name, seg) \ + { name, offsetof(CPUState, segs[seg].selector), NULL, MD_I32 },\ + { name ".base", offsetof(CPUState, segs[seg].base) },\ + { name ".limit", offsetof(CPUState, segs[seg].limit), NULL, MD_I32 }, + + { "eax", offsetof(CPUState, regs[0]) }, + { "ecx", offsetof(CPUState, regs[1]) }, + { "edx", offsetof(CPUState, regs[2]) }, + { "ebx", offsetof(CPUState, regs[3]) }, + { "esp|sp", offsetof(CPUState, regs[4]) }, + { "ebp|fp", offsetof(CPUState, regs[5]) }, + { "esi", offsetof(CPUState, regs[6]) }, + { "edi", offsetof(CPUState, regs[7]) }, +#ifdef TARGET_X86_64 + { "r8", offsetof(CPUState, regs[8]) }, + { "r9", offsetof(CPUState, regs[9]) }, + { "r10", offsetof(CPUState, regs[10]) }, + { "r11", offsetof(CPUState, regs[11]) }, + { "r12", offsetof(CPUState, regs[12]) }, + { "r13", offsetof(CPUState, regs[13]) }, + { "r14", offsetof(CPUState, regs[14]) }, + { "r15", offsetof(CPUState, regs[15]) }, +#endif + { "eflags", offsetof(CPUState, eflags) }, + { "eip", offsetof(CPUState, eip) }, + SEG("cs", R_CS) + SEG("ds", R_DS) + SEG("es", R_ES) + SEG("ss", R_SS) + SEG("fs", R_FS) + SEG("gs", R_GS) + { "pc", 0, monitor_get_pc, }, +#elif defined(TARGET_PPC) + { "r0", offsetof(CPUState, gpr[0]) }, + { "r1", offsetof(CPUState, gpr[1]) }, + { "r2", offsetof(CPUState, gpr[2]) }, + { "r3", offsetof(CPUState, gpr[3]) }, + { "r4", offsetof(CPUState, gpr[4]) }, + { "r5", offsetof(CPUState, gpr[5]) }, + { "r6", offsetof(CPUState, gpr[6]) }, + { "r7", offsetof(CPUState, gpr[7]) }, + { "r8", offsetof(CPUState, gpr[8]) }, + { "r9", offsetof(CPUState, gpr[9]) }, + { "r10", offsetof(CPUState, gpr[10]) }, + { "r11", offsetof(CPUState, gpr[11]) }, + { "r12", offsetof(CPUState, gpr[12]) }, + { "r13", offsetof(CPUState, gpr[13]) }, + { "r14", offsetof(CPUState, gpr[14]) }, + { "r15", offsetof(CPUState, gpr[15]) }, + { "r16", offsetof(CPUState, gpr[16]) }, + { "r17", offsetof(CPUState, gpr[17]) }, + { "r18", offsetof(CPUState, gpr[18]) }, + { "r19", offsetof(CPUState, gpr[19]) }, + { "r20", offsetof(CPUState, gpr[20]) }, + { "r21", offsetof(CPUState, gpr[21]) }, + { "r22", offsetof(CPUState, gpr[22]) }, + { "r23", offsetof(CPUState, gpr[23]) }, + { "r24", offsetof(CPUState, gpr[24]) }, + { "r25", offsetof(CPUState, gpr[25]) }, + { "r26", offsetof(CPUState, gpr[26]) }, + { "r27", offsetof(CPUState, gpr[27]) }, + { "r28", offsetof(CPUState, gpr[28]) }, + { "r29", offsetof(CPUState, gpr[29]) }, + { "r30", offsetof(CPUState, gpr[30]) }, + { "r31", offsetof(CPUState, gpr[31]) }, + { "nip|pc", offsetof(CPUState, nip) }, + { "lr", offsetof(CPUState, lr) }, + { "ctr", offsetof(CPUState, ctr) }, + { "decr", 0, &monitor_get_decr, }, + { "ccr", 0, &monitor_get_ccr, }, + { "msr", 0, &monitor_get_msr, }, + { "xer", 0, &monitor_get_xer, }, + { "tbu", 0, &monitor_get_tbu, }, + { "tbl", 0, &monitor_get_tbl, }, + { "sdr1", offsetof(CPUState, sdr1) }, + { "sr0", offsetof(CPUState, sr[0]) }, + { "sr1", offsetof(CPUState, sr[1]) }, + { "sr2", offsetof(CPUState, sr[2]) }, + { "sr3", offsetof(CPUState, sr[3]) }, + { "sr4", offsetof(CPUState, sr[4]) }, + { "sr5", offsetof(CPUState, sr[5]) }, + { "sr6", offsetof(CPUState, sr[6]) }, + { "sr7", offsetof(CPUState, sr[7]) }, + { "sr8", offsetof(CPUState, sr[8]) }, + { "sr9", offsetof(CPUState, sr[9]) }, + { "sr10", offsetof(CPUState, sr[10]) }, + { "sr11", offsetof(CPUState, sr[11]) }, + { "sr12", offsetof(CPUState, sr[12]) }, + { "sr13", offsetof(CPUState, sr[13]) }, + { "sr14", offsetof(CPUState, sr[14]) }, + { "sr15", offsetof(CPUState, sr[15]) }, + /* Too lazy to put BATs and SPRs ... */ +#elif defined(TARGET_SPARC) + { "g0", offsetof(CPUState, gregs[0]) }, + { "g1", offsetof(CPUState, gregs[1]) }, + { "g2", offsetof(CPUState, gregs[2]) }, + { "g3", offsetof(CPUState, gregs[3]) }, + { "g4", offsetof(CPUState, gregs[4]) }, + { "g5", offsetof(CPUState, gregs[5]) }, + { "g6", offsetof(CPUState, gregs[6]) }, + { "g7", offsetof(CPUState, gregs[7]) }, + { "o0", 0, monitor_get_reg }, + { "o1", 1, monitor_get_reg }, + { "o2", 2, monitor_get_reg }, + { "o3", 3, monitor_get_reg }, + { "o4", 4, monitor_get_reg }, + { "o5", 5, monitor_get_reg }, + { "o6", 6, monitor_get_reg }, + { "o7", 7, monitor_get_reg }, + { "l0", 8, monitor_get_reg }, + { "l1", 9, monitor_get_reg }, + { "l2", 10, monitor_get_reg }, + { "l3", 11, monitor_get_reg }, + { "l4", 12, monitor_get_reg }, + { "l5", 13, monitor_get_reg }, + { "l6", 14, monitor_get_reg }, + { "l7", 15, monitor_get_reg }, + { "i0", 16, monitor_get_reg }, + { "i1", 17, monitor_get_reg }, + { "i2", 18, monitor_get_reg }, + { "i3", 19, monitor_get_reg }, + { "i4", 20, monitor_get_reg }, + { "i5", 21, monitor_get_reg }, + { "i6", 22, monitor_get_reg }, + { "i7", 23, monitor_get_reg }, + { "pc", offsetof(CPUState, pc) }, + { "npc", offsetof(CPUState, npc) }, + { "y", offsetof(CPUState, y) }, +#ifndef TARGET_SPARC64 + { "psr", 0, &monitor_get_psr, }, + { "wim", offsetof(CPUState, wim) }, +#endif + { "tbr", offsetof(CPUState, tbr) }, + { "fsr", offsetof(CPUState, fsr) }, + { "f0", offsetof(CPUState, fpr[0]) }, + { "f1", offsetof(CPUState, fpr[1]) }, + { "f2", offsetof(CPUState, fpr[2]) }, + { "f3", offsetof(CPUState, fpr[3]) }, + { "f4", offsetof(CPUState, fpr[4]) }, + { "f5", offsetof(CPUState, fpr[5]) }, + { "f6", offsetof(CPUState, fpr[6]) }, + { "f7", offsetof(CPUState, fpr[7]) }, + { "f8", offsetof(CPUState, fpr[8]) }, + { "f9", offsetof(CPUState, fpr[9]) }, + { "f10", offsetof(CPUState, fpr[10]) }, + { "f11", offsetof(CPUState, fpr[11]) }, + { "f12", offsetof(CPUState, fpr[12]) }, + { "f13", offsetof(CPUState, fpr[13]) }, + { "f14", offsetof(CPUState, fpr[14]) }, + { "f15", offsetof(CPUState, fpr[15]) }, + { "f16", offsetof(CPUState, fpr[16]) }, + { "f17", offsetof(CPUState, fpr[17]) }, + { "f18", offsetof(CPUState, fpr[18]) }, + { "f19", offsetof(CPUState, fpr[19]) }, + { "f20", offsetof(CPUState, fpr[20]) }, + { "f21", offsetof(CPUState, fpr[21]) }, + { "f22", offsetof(CPUState, fpr[22]) }, + { "f23", offsetof(CPUState, fpr[23]) }, + { "f24", offsetof(CPUState, fpr[24]) }, + { "f25", offsetof(CPUState, fpr[25]) }, + { "f26", offsetof(CPUState, fpr[26]) }, + { "f27", offsetof(CPUState, fpr[27]) }, + { "f28", offsetof(CPUState, fpr[28]) }, + { "f29", offsetof(CPUState, fpr[29]) }, + { "f30", offsetof(CPUState, fpr[30]) }, + { "f31", offsetof(CPUState, fpr[31]) }, +#ifdef TARGET_SPARC64 + { "f32", offsetof(CPUState, fpr[32]) }, + { "f34", offsetof(CPUState, fpr[34]) }, + { "f36", offsetof(CPUState, fpr[36]) }, + { "f38", offsetof(CPUState, fpr[38]) }, + { "f40", offsetof(CPUState, fpr[40]) }, + { "f42", offsetof(CPUState, fpr[42]) }, + { "f44", offsetof(CPUState, fpr[44]) }, + { "f46", offsetof(CPUState, fpr[46]) }, + { "f48", offsetof(CPUState, fpr[48]) }, + { "f50", offsetof(CPUState, fpr[50]) }, + { "f52", offsetof(CPUState, fpr[52]) }, + { "f54", offsetof(CPUState, fpr[54]) }, + { "f56", offsetof(CPUState, fpr[56]) }, + { "f58", offsetof(CPUState, fpr[58]) }, + { "f60", offsetof(CPUState, fpr[60]) }, + { "f62", offsetof(CPUState, fpr[62]) }, + { "asi", offsetof(CPUState, asi) }, + { "pstate", offsetof(CPUState, pstate) }, + { "cansave", offsetof(CPUState, cansave) }, + { "canrestore", offsetof(CPUState, canrestore) }, + { "otherwin", offsetof(CPUState, otherwin) }, + { "wstate", offsetof(CPUState, wstate) }, + { "cleanwin", offsetof(CPUState, cleanwin) }, + { "fprs", offsetof(CPUState, fprs) }, +#endif +#endif + { NULL }, +}; + +static void expr_error(const char *fmt) +{ + term_printf(fmt); + term_printf("\n"); + longjmp(expr_env, 1); +} + +/* return 0 if OK, -1 if not found, -2 if no CPU defined */ +static int get_monitor_def(target_long *pval, const char *name) +{ + MonitorDef *md; + void *ptr; + + for(md = monitor_defs; md->name != NULL; md++) { + if (compare_cmd(name, md->name)) { + if (md->get_value) { + *pval = md->get_value(md, md->offset); + } else { + CPUState *env = mon_get_cpu(); + if (!env) + return -2; + ptr = (uint8_t *)env + md->offset; + switch(md->type) { + case MD_I32: + *pval = *(int32_t *)ptr; + break; + case MD_TLONG: + *pval = *(target_long *)ptr; + break; + default: + *pval = 0; + break; + } + } + return 0; + } + } + return -1; +} + +static void next(void) +{ + if (pch != '\0') { + pch++; + while (isspace(*pch)) + pch++; + } +} + +static target_long expr_sum(void); + +static target_long expr_unary(void) +{ + target_long n; + char *p; + int ret; + + switch(*pch) { + case '+': + next(); + n = expr_unary(); + break; + case '-': + next(); + n = -expr_unary(); + break; + case '~': + next(); + n = ~expr_unary(); + break; + case '(': + next(); + n = expr_sum(); + if (*pch != ')') { + expr_error("')' expected"); + } + next(); + break; + case '\'': + pch++; + if (*pch == '\0') + expr_error("character constant expected"); + n = *pch; + pch++; + if (*pch != '\'') + expr_error("missing terminating \' character"); + next(); + break; + case '$': + { + char buf[128], *q; + + pch++; + q = buf; + while ((*pch >= 'a' && *pch <= 'z') || + (*pch >= 'A' && *pch <= 'Z') || + (*pch >= '0' && *pch <= '9') || + *pch == '_' || *pch == '.') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = *pch; + pch++; + } + while (isspace(*pch)) + pch++; + *q = 0; + ret = get_monitor_def(&n, buf); + if (ret == -1) + expr_error("unknown register"); + else if (ret == -2) + expr_error("no cpu defined"); + } + break; + case '\0': + expr_error("unexpected end of expression"); + n = 0; + break; + default: + /* XXX: 64 bit version */ + n = strtoul(pch, &p, 0); + if (pch == p) { + expr_error("invalid char in expression"); + } + pch = p; + while (isspace(*pch)) + pch++; + break; + } + return n; +} + + +static target_long expr_prod(void) +{ + target_long val, val2; + int op; + + val = expr_unary(); + for(;;) { + op = *pch; + if (op != '*' && op != '/' && op != '%') + break; + next(); + val2 = expr_unary(); + switch(op) { + default: + case '*': + val *= val2; + break; + case '/': + case '%': + if (val2 == 0) + expr_error("division by zero"); + if (op == '/') + val /= val2; + else + val %= val2; + break; + } + } + return val; +} + +static target_long expr_logic(void) +{ + target_long val, val2; + int op; + + val = expr_prod(); + for(;;) { + op = *pch; + if (op != '&' && op != '|' && op != '^') + break; + next(); + val2 = expr_prod(); + switch(op) { + default: + case '&': + val &= val2; + break; + case '|': + val |= val2; + break; + case '^': + val ^= val2; + break; + } + } + return val; +} + +static target_long expr_sum(void) +{ + target_long val, val2; + int op; + + val = expr_logic(); + for(;;) { + op = *pch; + if (op != '+' && op != '-') + break; + next(); + val2 = expr_logic(); + if (op == '+') + val += val2; + else + val -= val2; + } + return val; +} + +static int get_expr(target_long *pval, const char **pp) +{ + pch = *pp; + if (setjmp(expr_env)) { + *pp = pch; + return -1; + } + while (isspace(*pch)) + pch++; + *pval = expr_sum(); + *pp = pch; + return 0; +} +#endif /* !CONFIG_DM */ + +static int get_str(char *buf, int buf_size, const char **pp) +{ + const char *p; + char *q; + int c; + + q = buf; + p = *pp; + while (isspace(*p)) + p++; + if (*p == '\0') { + fail: + *q = '\0'; + *pp = p; + return -1; + } + if (*p == '\"') { + p++; + while (*p != '\0' && *p != '\"') { + if (*p == '\\') { + p++; + c = *p++; + switch(c) { + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case '\\': + case '\'': + case '\"': + break; + default: + qemu_printf("unsupported escape code: '\\%c'\n", c); + goto fail; + } + if ((q - buf) < buf_size - 1) { + *q++ = c; + } + } else { + if ((q - buf) < buf_size - 1) { + *q++ = *p; + } + p++; + } + } + if (*p != '\"') { + qemu_printf("unterminated string\n"); + goto fail; + } + p++; + } else { + while (*p != '\0' && !isspace(*p)) { + if ((q - buf) < buf_size - 1) { + *q++ = *p; + } + p++; + } + } + *q = '\0'; + *pp = p; + return 0; +} + +#ifndef CONFIG_DM +static int default_fmt_format = 'x'; +static int default_fmt_size = 4; +#endif /* !CONFIG_DM */ + +#define MAX_ARGS 16 + +static void monitor_handle_command(const char *cmdline) +{ + const char *p, *pstart, *typestr; + char *q; + int c, nb_args, len, i; +#ifndef CONFIG_DM + int has_arg; +#endif /* !CONFIG_DM */ + term_cmd_t *cmd; + char cmdname[256]; + char buf[1024]; + void *str_allocated[MAX_ARGS]; + void *args[MAX_ARGS]; + +#ifdef DEBUG + term_printf("command='%s'\n", cmdline); +#endif + + /* extract the command name */ + p = cmdline; + q = cmdname; + while (isspace(*p)) + p++; + if (*p == '\0') + return; + pstart = p; + while (*p != '\0' && *p != '/' && !isspace(*p)) + p++; + len = p - pstart; + if (len > sizeof(cmdname) - 1) + len = sizeof(cmdname) - 1; + memcpy(cmdname, pstart, len); + cmdname[len] = '\0'; + + /* find the command */ + for(cmd = term_cmds; cmd->name != NULL; cmd++) { + if (compare_cmd(cmdname, cmd->name)) + goto found; + } + term_printf("unknown command: '%s'\n", cmdname); + return; + found: + + for(i = 0; i < MAX_ARGS; i++) + str_allocated[i] = NULL; + + /* parse the parameters */ + typestr = cmd->args_type; + nb_args = 0; + for(;;) { + c = *typestr; + if (c == '\0') + break; + typestr++; + switch(c) { + case 'F': + case 'B': + case 's': + { + int ret; + char *str; + + while (isspace(*p)) + p++; + if (*typestr == '?') { + typestr++; + if (*p == '\0') { + /* no optional string: NULL argument */ + str = NULL; + goto add_str; + } + } + ret = get_str(buf, sizeof(buf), &p); + if (ret < 0) { + switch(c) { + case 'F': + term_printf("%s: filename expected\n", cmdname); + break; + case 'B': + term_printf("%s: block device name expected\n", cmdname); + break; + default: + term_printf("%s: string expected\n", cmdname); + break; + } + goto fail; + } + str = qemu_malloc(strlen(buf) + 1); + strcpy(str, buf); + str_allocated[nb_args] = str; + add_str: + if (nb_args >= MAX_ARGS) { + error_args: + term_printf("%s: too many arguments\n", cmdname); + goto fail; + } + args[nb_args++] = str; + } + break; +#ifndef CONFIG_DM + case '/': + { + int count, format, size; + + while (isspace(*p)) + p++; + if (*p == '/') { + /* format found */ + p++; + count = 1; + if (isdigit(*p)) { + count = 0; + while (isdigit(*p)) { + count = count * 10 + (*p - '0'); + p++; + } + } + size = -1; + format = -1; + for(;;) { + switch(*p) { + case 'o': + case 'd': + case 'u': + case 'x': + case 'i': + case 'c': + format = *p++; + break; + case 'b': + size = 1; + p++; + break; + case 'h': + size = 2; + p++; + break; + case 'w': + size = 4; + p++; + break; + case 'g': + case 'L': + size = 8; + p++; + break; + default: + goto next; + } + } + next: + if (*p != '\0' && !isspace(*p)) { + term_printf("invalid char in format: '%c'\n", *p); + goto fail; + } + if (format < 0) + format = default_fmt_format; + if (format != 'i') { + /* for 'i', not specifying a size gives -1 as size */ + if (size < 0) + size = default_fmt_size; + } + default_fmt_size = size; + default_fmt_format = format; + } else { + count = 1; + format = default_fmt_format; + if (format != 'i') { + size = default_fmt_size; + } else { + size = -1; + } + } + if (nb_args + 3 > MAX_ARGS) + goto error_args; + args[nb_args++] = (void*)count; + args[nb_args++] = (void*)format; + args[nb_args++] = (void*)size; + } + break; + case 'i': + case 'l': + { + target_long val; + while (isspace(*p)) + p++; + if (*typestr == '?' || *typestr == '.') { + typestr++; + if (*typestr == '?') { + if (*p == '\0') + has_arg = 0; + else + has_arg = 1; + } else { + if (*p == '.') { + p++; + while (isspace(*p)) + p++; + has_arg = 1; + } else { + has_arg = 0; + } + } + if (nb_args >= MAX_ARGS) + goto error_args; + args[nb_args++] = (void *)has_arg; + if (!has_arg) { + if (nb_args >= MAX_ARGS) + goto error_args; + val = -1; + goto add_num; + } + } + if (get_expr(&val, &p)) + goto fail; + add_num: + if (c == 'i') { + if (nb_args >= MAX_ARGS) + goto error_args; + args[nb_args++] = (void *)(int)val; + } else { + if ((nb_args + 1) >= MAX_ARGS) + goto error_args; +#if TARGET_LONG_BITS == 64 + args[nb_args++] = (void *)(int)((val >> 32) & 0xffffffff); +#else + args[nb_args++] = (void *)0; +#endif + args[nb_args++] = (void *)(int)(val & 0xffffffff); + } + } + break; +#endif /* !CONFIG_DM */ + case '-': + { + int has_option; + /* option */ + + c = *typestr++; + if (c == '\0') + goto bad_type; + while (isspace(*p)) + p++; + has_option = 0; + if (*p == '-') { + p++; + if (*p != c) { + term_printf("%s: unsupported option -%c\n", + cmdname, *p); + goto fail; + } + p++; + has_option = 1; + } + if (nb_args >= MAX_ARGS) + goto error_args; + args[nb_args++] = (void *)has_option; + } + break; +#ifdef CONFIG_DM + /* TODO: add more commands we need here to support hvm device model */ + case '/': + case 'i': +#endif /* CONFIG_DM */ + default: + bad_type: + term_printf("%s: unknown type '%c'\n", cmdname, c); + goto fail; + } + } + /* check that all arguments were parsed */ + while (isspace(*p)) + p++; + if (*p != '\0') { + term_printf("%s: extraneous characters at the end of line\n", + cmdname); + goto fail; + } + + switch(nb_args) { + case 0: + cmd->handler(); + break; + case 1: + cmd->handler(args[0]); + break; + case 2: + cmd->handler(args[0], args[1]); + break; + case 3: + cmd->handler(args[0], args[1], args[2]); + break; + case 4: + cmd->handler(args[0], args[1], args[2], args[3]); + break; + case 5: + cmd->handler(args[0], args[1], args[2], args[3], args[4]); + break; + case 6: + cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5]); + break; + default: + term_printf("unsupported number of arguments: %d\n", nb_args); + goto fail; + } + fail: + for(i = 0; i < MAX_ARGS; i++) + qemu_free(str_allocated[i]); + return; +} + +#ifndef CONFIG_DM +static void cmd_completion(const char *name, const char *list) +{ + const char *p, *pstart; + char cmd[128]; + int len; + + p = list; + for(;;) { + pstart = p; + p = strchr(p, '|'); + if (!p) + p = pstart + strlen(pstart); + len = p - pstart; + if (len > sizeof(cmd) - 2) + len = sizeof(cmd) - 2; + memcpy(cmd, pstart, len); + cmd[len] = '\0'; + if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) { + add_completion(cmd); + } + if (*p == '\0') + break; + p++; + } +} + +static void file_completion(const char *input) +{ + DIR *ffs; + struct dirent *d; + char path[1024]; + char file[1024], file_prefix[1024]; + int input_path_len; + const char *p; + + p = strrchr(input, '/'); + if (!p) { + input_path_len = 0; + pstrcpy(file_prefix, sizeof(file_prefix), input); + strcpy(path, "."); + } else { + input_path_len = p - input + 1; + memcpy(path, input, input_path_len); + if (input_path_len > sizeof(path) - 1) + input_path_len = sizeof(path) - 1; + path[input_path_len] = '\0'; + pstrcpy(file_prefix, sizeof(file_prefix), p + 1); + } +#ifdef DEBUG_COMPLETION + term_printf("input='%s' path='%s' prefix='%s'\n", input, path, file_prefix); +#endif + ffs = opendir(path); + if (!ffs) + return; + for(;;) { + struct stat sb; + d = readdir(ffs); + if (!d) + break; + if (strstart(d->d_name, file_prefix, NULL)) { + memcpy(file, input, input_path_len); + strcpy(file + input_path_len, d->d_name); + /* stat the file to find out if it's a directory. + * In that case add a slash to speed up typing long paths + */ + stat(file, &sb); + if(S_ISDIR(sb.st_mode)) + strcat(file, "/"); + add_completion(file); + } + } + closedir(ffs); +} + +static void block_completion_it(void *opaque, const char *name) +{ + const char *input = opaque; + + if (input[0] == '\0' || + !strncmp(name, (char *)input, strlen(input))) { + add_completion(name); + } +} + +/* NOTE: this parser is an approximate form of the real command parser */ +static void parse_cmdline(const char *cmdline, + int *pnb_args, char **args) +{ + const char *p; + int nb_args, ret; + char buf[1024]; + + p = cmdline; + nb_args = 0; + for(;;) { + while (isspace(*p)) + p++; + if (*p == '\0') + break; + if (nb_args >= MAX_ARGS) + break; + ret = get_str(buf, sizeof(buf), &p); + args[nb_args] = qemu_strdup(buf); + nb_args++; + if (ret < 0) + break; + } + *pnb_args = nb_args; +} + +void readline_find_completion(const char *cmdline) +{ + const char *cmdname; + char *args[MAX_ARGS]; + int nb_args, i, len; + const char *ptype, *str; + term_cmd_t *cmd; + + parse_cmdline(cmdline, &nb_args, args); +#ifdef DEBUG_COMPLETION + for(i = 0; i < nb_args; i++) { + term_printf("arg%d = '%s'\n", i, (char *)args[i]); + } +#endif + + /* if the line ends with a space, it means we want to complete the + next arg */ + len = strlen(cmdline); + if (len > 0 && isspace(cmdline[len - 1])) { + if (nb_args >= MAX_ARGS) + return; + args[nb_args++] = qemu_strdup(""); + } + if (nb_args <= 1) { + /* command completion */ + if (nb_args == 0) + cmdname = ""; + else + cmdname = args[0]; + completion_index = strlen(cmdname); + for(cmd = term_cmds; cmd->name != NULL; cmd++) { + cmd_completion(cmdname, cmd->name); + } + } else { + /* find the command */ + for(cmd = term_cmds; cmd->name != NULL; cmd++) { + if (compare_cmd(args[0], cmd->name)) + goto found; + } + return; + found: + ptype = cmd->args_type; + for(i = 0; i < nb_args - 2; i++) { + if (*ptype != '\0') { + ptype++; + while (*ptype == '?') + ptype++; + } + } + str = args[nb_args - 1]; + switch(*ptype) { + case 'F': + /* file completion */ + completion_index = strlen(str); + file_completion(str); + break; + case 'B': + /* block device name completion */ + completion_index = strlen(str); + bdrv_iterate(block_completion_it, (void *)str); + break; + case 's': + /* XXX: more generic ? */ + if (!strcmp(cmd->name, "info")) { + completion_index = strlen(str); + for(cmd = info_cmds; cmd->name != NULL; cmd++) { + cmd_completion(str, cmd->name); + } + } + break; + default: + break; + } + } + for(i = 0; i < nb_args; i++) + qemu_free(args[i]); +} +#else +void readline_find_completion(const char *cmdline) +{ +} +#endif /* !CONFIG_DM */ + +static int term_can_read(void *opaque) +{ + return 128; +} + +static void term_read(void *opaque, const uint8_t *buf, int size) +{ + int i; + for(i = 0; i < size; i++) + readline_handle_byte(buf[i]); +} + +static void monitor_start_input(void); + +static void monitor_handle_command1(void *opaque, const char *cmdline) +{ + monitor_handle_command(cmdline); + monitor_start_input(); +} + +static void monitor_start_input(void) +{ + readline_start("(HVMXen) ", 0, monitor_handle_command1, NULL); +} + +void monitor_init(CharDriverState *hd, int show_banner) +{ + monitor_hd = hd; + if (show_banner) { + term_printf("HVM device model. type 'q' to exit\n"); + } + qemu_chr_add_read_handler(hd, term_can_read, term_read, NULL); + monitor_start_input(); +} + +/* XXX: use threads ? */ +/* modal monitor readline */ +static int monitor_readline_started; +static char *monitor_readline_buf; +static int monitor_readline_buf_size; + +static void monitor_readline_cb(void *opaque, const char *input) +{ + pstrcpy(monitor_readline_buf, monitor_readline_buf_size, input); + monitor_readline_started = 0; +} + +void monitor_readline(const char *prompt, int is_password, + char *buf, int buf_size) +{ + if (is_password) { + qemu_chr_send_event(monitor_hd, CHR_EVENT_FOCUS); + } + readline_start(prompt, is_password, monitor_readline_cb, NULL); + monitor_readline_buf = buf; + monitor_readline_buf_size = buf_size; + monitor_readline_started = 1; + while (monitor_readline_started) { + main_loop_wait(10); + } +} diff --git a/tools/ioemu/osdep.c b/tools/ioemu/osdep.c new file mode 100644 index 0000000000..f2a69d9a71 --- /dev/null +++ b/tools/ioemu/osdep.c @@ -0,0 +1,628 @@ +/* + * QEMU low level functions + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include + +#include "cpu.h" + +#if defined(__i386__) && !defined(CONFIG_SOFTMMU) && !defined(CONFIG_USER_ONLY) + +#include +#include + +/* When not using soft mmu, libc independant functions are needed for + the CPU core because it needs to use alternates stacks and + libc/thread incompatibles settings */ + +#include + +#define QEMU_SYSCALL0(name) \ +{ \ +long __res; \ +__asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name)); \ +return __res; \ +} + +#define QEMU_SYSCALL1(name,arg1) \ +{ \ +long __res; \ +__asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"b" ((long)(arg1))); \ +return __res; \ +} + +#define QEMU_SYSCALL2(name,arg1,arg2) \ +{ \ +long __res; \ +__asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \ +return __res; \ +} + +#define QEMU_SYSCALL3(name,arg1,arg2,arg3) \ +{ \ +long __res; \ +__asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \ + "d" ((long)(arg3))); \ +return __res; \ +} + +#define QEMU_SYSCALL4(name,arg1,arg2,arg3,arg4) \ +{ \ +long __res; \ +__asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \ + "d" ((long)(arg3)),"S" ((long)(arg4))); \ +return __res; \ +} + +#define QEMU_SYSCALL5(name,arg1,arg2,arg3,arg4,arg5) \ +{ \ +long __res; \ +__asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \ + "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5))); \ +return __res; \ +} + +#define QEMU_SYSCALL6(name,arg1,arg2,arg3,arg4,arg5,arg6) \ +{ \ +long __res; \ +__asm__ volatile ("push %%ebp ; movl %%eax,%%ebp ; movl %1,%%eax ; int $0x80 ; pop %%ebp" \ + : "=a" (__res) \ + : "i" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \ + "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5)), \ + "0" ((long)(arg6))); \ +return __res; \ +} + +int qemu_write(int fd, const void *buf, size_t n) +{ + QEMU_SYSCALL3(write, fd, buf, n); +} + + + +/****************************************************************/ +/* shmat replacement */ + +int qemu_ipc(int call, unsigned long first, + unsigned long second, unsigned long third, + void *ptr, unsigned long fifth) +{ + QEMU_SYSCALL6(ipc, call, first, second, third, ptr, fifth); +} + +#define SHMAT 21 + +/* we must define shmat so that a specific address will be used when + mapping the X11 ximage */ +void *shmat(int shmid, const void *shmaddr, int shmflg) +{ + void *ptr; + int ret; + /* we give an address in the right memory area */ + if (!shmaddr) + shmaddr = get_mmap_addr(8192 * 1024); + ret = qemu_ipc(SHMAT, shmid, shmflg, (unsigned long)&ptr, (void *)shmaddr, 0); + if (ret < 0) + return NULL; + return ptr; +} + +/****************************************************************/ +/* sigaction bypassing the threads */ + +static int kernel_sigaction(int signum, const struct qemu_sigaction *act, + struct qemu_sigaction *oldact, + int sigsetsize) +{ + QEMU_SYSCALL4(rt_sigaction, signum, act, oldact, sigsetsize); +} + +int qemu_sigaction(int signum, const struct qemu_sigaction *act, + struct qemu_sigaction *oldact) +{ + return kernel_sigaction(signum, act, oldact, 8); +} + +/****************************************************************/ +/* memory allocation */ + +//#define DEBUG_MALLOC + +#define MALLOC_BASE 0xab000000 +#define PHYS_RAM_BASE 0xac000000 + +#define MALLOC_ALIGN 16 +#define BLOCK_HEADER_SIZE 16 + +typedef struct MemoryBlock { + struct MemoryBlock *next; + unsigned long size; /* size of block, including header */ +} MemoryBlock; + +static MemoryBlock *first_free_block; +static unsigned long malloc_addr = MALLOC_BASE; + +static void *malloc_get_space(size_t size) +{ + void *ptr; + size = TARGET_PAGE_ALIGN(size); + ptr = mmap((void *)malloc_addr, size, + PROT_WRITE | PROT_READ, + MAP_PRIVATE | MAP_FIXED | MAP_ANON, -1, 0); + if (ptr == MAP_FAILED) + return NULL; + malloc_addr += size; + return ptr; +} + +void *qemu_malloc(size_t size) +{ + MemoryBlock *mb, *mb1, **pmb; + void *ptr; + size_t size1, area_size; + + if (size == 0) + return NULL; + + size = (size + BLOCK_HEADER_SIZE + MALLOC_ALIGN - 1) & ~(MALLOC_ALIGN - 1); + pmb = &first_free_block; + for(;;) { + mb = *pmb; + if (mb == NULL) + break; + if (size <= mb->size) + goto found; + pmb = &mb->next; + } + /* no big enough blocks found: get new space */ + area_size = TARGET_PAGE_ALIGN(size); + mb = malloc_get_space(area_size); + if (!mb) + return NULL; + size1 = area_size - size; + if (size1 > 0) { + /* create a new free block */ + mb1 = (MemoryBlock *)((uint8_t *)mb + size); + mb1->next = NULL; + mb1->size = size1; + *pmb = mb1; + } + goto the_end; + found: + /* a free block was found: use it */ + size1 = mb->size - size; + if (size1 > 0) { + /* create a new free block */ + mb1 = (MemoryBlock *)((uint8_t *)mb + size); + mb1->next = mb->next; + mb1->size = size1; + *pmb = mb1; + } else { + /* suppress the first block */ + *pmb = mb->next; + } + the_end: + mb->size = size; + mb->next = NULL; + ptr = ((uint8_t *)mb + BLOCK_HEADER_SIZE); +#ifdef DEBUG_MALLOC + qemu_printf("malloc: size=0x%x ptr=0x%lx\n", size, (unsigned long)ptr); +#endif + return ptr; +} + +void qemu_free(void *ptr) +{ + MemoryBlock *mb; + + if (!ptr) + return; + mb = (MemoryBlock *)((uint8_t *)ptr - BLOCK_HEADER_SIZE); + mb->next = first_free_block; + first_free_block = mb; +} + +/****************************************************************/ +/* virtual memory allocation */ + +unsigned long mmap_addr = PHYS_RAM_BASE; + +void *get_mmap_addr(unsigned long size) +{ + unsigned long addr; + addr = mmap_addr; + mmap_addr += ((size + 4095) & ~4095) + 4096; + return (void *)addr; +} + +#else + +#ifdef _WIN32 +#include +#elif defined(_BSD) +#include +#else +#include +#endif + +int qemu_write(int fd, const void *buf, size_t n) +{ + int ret; + ret = write(fd, buf, n); + if (ret < 0) + return -errno; + else + return ret; +} + +void *get_mmap_addr(unsigned long size) +{ + return NULL; +} + +void qemu_free(void *ptr) +{ + free(ptr); +} + +void *qemu_malloc(size_t size) +{ + return malloc(size); +} + +#if defined(_WIN32) + +void *qemu_vmalloc(size_t size) +{ + /* FIXME: this is not exactly optimal solution since VirtualAlloc + has 64Kb granularity, but at least it guarantees us that the + memory is page aligned. */ + return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); +} + +void qemu_vfree(void *ptr) +{ + VirtualFree(ptr, 0, MEM_RELEASE); +} + +#elif defined(USE_KQEMU) + +#include +#include +#include + +void *qemu_vmalloc(size_t size) +{ + static int phys_ram_fd = -1; + static int phys_ram_size = 0; + const char *tmpdir; + char phys_ram_file[1024]; + void *ptr; + struct statfs stfs; + + if (phys_ram_fd < 0) { + tmpdir = getenv("QEMU_TMPDIR"); + if (!tmpdir) + tmpdir = "/dev/shm"; + if (statfs(tmpdir, &stfs) == 0) { + int64_t free_space; + int ram_mb; + + extern int ram_size; + free_space = (int64_t)stfs.f_bavail * stfs.f_bsize; + if ((ram_size + 8192 * 1024) >= free_space) { + ram_mb = (ram_size / (1024 * 1024)); + fprintf(stderr, + "You do not have enough space in '%s' for the %d MB of QEMU virtual RAM.\n", + tmpdir, ram_mb); + if (strcmp(tmpdir, "/dev/shm") == 0) { + fprintf(stderr, "To have more space available provided you have enough RAM and swap, do as root:\n" + "umount /dev/shm\n" + "mount -t tmpfs -o size=%dm none /dev/shm\n", + ram_mb + 16); + } else { + fprintf(stderr, + "Use the '-m' option of QEMU to diminish the amount of virtual RAM or use the\n" + "QEMU_TMPDIR environment variable to set another directory where the QEMU\n" + "temporary RAM file will be opened.\n"); + } + exit(1); + } + } + snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/qemuXXXXXX", + tmpdir); + if (mkstemp(phys_ram_file) < 0) { + fprintf(stderr, + "warning: could not create temporary file in '%s'.\n" + "Use QEMU_TMPDIR to select a directory in a tmpfs filesystem.\n" + "Using '/tmp' as fallback.\n", + tmpdir); + snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/qemuXXXXXX", + "/tmp"); + if (mkstemp(phys_ram_file) < 0) { + fprintf(stderr, "Could not create temporary memory file '%s'\n", + phys_ram_file); + exit(1); + } + } + phys_ram_fd = open(phys_ram_file, O_CREAT | O_TRUNC | O_RDWR, 0600); + if (phys_ram_fd < 0) { + fprintf(stderr, "Could not open temporary memory file '%s'\n", + phys_ram_file); + exit(1); + } + unlink(phys_ram_file); + } + size = (size + 4095) & ~4095; + ftruncate(phys_ram_fd, phys_ram_size + size); + ptr = mmap(NULL, + size, + PROT_WRITE | PROT_READ, MAP_SHARED, + phys_ram_fd, phys_ram_size); + if (ptr == MAP_FAILED) { + fprintf(stderr, "Could not map physical memory\n"); + exit(1); + } + phys_ram_size += size; + return ptr; +} + +void qemu_vfree(void *ptr) +{ + /* may be useful some day, but currently we do not need to free */ +} + +#else + +/* alloc shared memory pages */ +void *qemu_vmalloc(size_t size) +{ +#ifdef _BSD + return valloc(size); +#else + return memalign(4096, size); +#endif +} + +void qemu_vfree(void *ptr) +{ + free(ptr); +} + +#endif + +#endif + +void *qemu_mallocz(size_t size) +{ + void *ptr; + ptr = qemu_malloc(size); + if (!ptr) + return NULL; + memset(ptr, 0, size); + return ptr; +} + +char *qemu_strdup(const char *str) +{ + char *ptr; + ptr = qemu_malloc(strlen(str) + 1); + if (!ptr) + return NULL; + strcpy(ptr, str); + return ptr; +} + +/****************************************************************/ +/* printf support */ + +static inline int qemu_isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) + +/* from BSD ppp sources */ +int qemu_vsnprintf(char *buf, int buflen, const char *fmt, va_list args) +{ + int c, i, n; + int width, prec, fillch; + int base, len, neg; + unsigned long val = 0; + const char *f; + char *str, *buf0; + char num[32]; + static const char hexchars[] = "0123456789abcdef"; + + buf0 = buf; + --buflen; + while (buflen > 0) { + for (f = fmt; *f != '%' && *f != 0; ++f) + ; + if (f > fmt) { + len = f - fmt; + if (len > buflen) + len = buflen; + memcpy(buf, fmt, len); + buf += len; + buflen -= len; + fmt = f; + } + if (*fmt == 0) + break; + c = *++fmt; + width = prec = 0; + fillch = ' '; + if (c == '0') { + fillch = '0'; + c = *++fmt; + } + if (c == '*') { + width = va_arg(args, int); + c = *++fmt; + } else { + while (qemu_isdigit(c)) { + width = width * 10 + c - '0'; + c = *++fmt; + } + } + if (c == '.') { + c = *++fmt; + if (c == '*') { + prec = va_arg(args, int); + c = *++fmt; + } else { + while (qemu_isdigit(c)) { + prec = prec * 10 + c - '0'; + c = *++fmt; + } + } + } + /* modifiers */ + switch(c) { + case 'l': + c = *++fmt; + break; + default: + break; + } + str = 0; + base = 0; + neg = 0; + ++fmt; + switch (c) { + case 'd': + i = va_arg(args, int); + if (i < 0) { + neg = 1; + val = -i; + } else + val = i; + base = 10; + break; + case 'o': + val = va_arg(args, unsigned int); + base = 8; + break; + case 'x': + case 'X': + val = va_arg(args, unsigned int); + base = 16; + break; + case 'p': + val = (unsigned long) va_arg(args, void *); + base = 16; + neg = 2; + break; + case 's': + str = va_arg(args, char *); + break; + case 'c': + num[0] = va_arg(args, int); + num[1] = 0; + str = num; + break; + default: + *buf++ = '%'; + if (c != '%') + --fmt; /* so %z outputs %z etc. */ + --buflen; + continue; + } + if (base != 0) { + str = num + sizeof(num); + *--str = 0; + while (str > num + neg) { + *--str = hexchars[val % base]; + val = val / base; + if (--prec <= 0 && val == 0) + break; + } + switch (neg) { + case 1: + *--str = '-'; + break; + case 2: + *--str = 'x'; + *--str = '0'; + break; + } + len = num + sizeof(num) - 1 - str; + } else { + len = strlen(str); + if (prec > 0 && len > prec) + len = prec; + } + if (width > 0) { + if (width > buflen) + width = buflen; + if ((n = width - len) > 0) { + buflen -= n; + for (; n > 0; --n) + *buf++ = fillch; + } + } + if (len > buflen) + len = buflen; + memcpy(buf, str, len); + buf += len; + buflen -= len; + } + *buf = 0; + return buf - buf0; +} + +void qemu_vprintf(const char *fmt, va_list ap) +{ + char buf[1024]; + int len; + + len = qemu_vsnprintf(buf, sizeof(buf), fmt, ap); + qemu_write(1, buf, len); +} + +void qemu_printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + qemu_vprintf(fmt, ap); + va_end(ap); +} + diff --git a/tools/ioemu/osdep.h b/tools/ioemu/osdep.h new file mode 100644 index 0000000000..f818ce03a8 --- /dev/null +++ b/tools/ioemu/osdep.h @@ -0,0 +1,53 @@ +#ifndef QEMU_OSDEP_H +#define QEMU_OSDEP_H + +#include + +int qemu_vsnprintf(char *buf, int buflen, const char *fmt, va_list args); +void qemu_vprintf(const char *fmt, va_list ap); +void qemu_printf(const char *fmt, ...); + +void *qemu_malloc(size_t size); +void *qemu_mallocz(size_t size); +void qemu_free(void *ptr); +char *qemu_strdup(const char *str); + +void *qemu_vmalloc(size_t size); +void qemu_vfree(void *ptr); + +void *get_mmap_addr(unsigned long size); + +/* specific kludges for OS compatibility (should be moved elsewhere) */ +#if defined(__i386__) && !defined(CONFIG_SOFTMMU) && !defined(CONFIG_USER_ONLY) + +/* disabled pthread version of longjmp which prevent us from using an + alternative signal stack */ +extern void __longjmp(jmp_buf env, int val); +#define longjmp __longjmp + +#include + +/* NOTE: it works only because the glibc sigset_t is >= kernel sigset_t */ +struct qemu_sigaction { + union { + void (*_sa_handler)(int); + void (*_sa_sigaction)(int, struct siginfo *, void *); + } _u; + unsigned long sa_flags; + void (*sa_restorer)(void); + sigset_t sa_mask; /* mask last for extensibility */ +}; + +int qemu_sigaction(int signum, const struct qemu_sigaction *act, + struct qemu_sigaction *oldact); + +#undef sigaction +#undef sa_handler +#undef sa_sigaction +#define sigaction qemu_sigaction +#define sa_handler _u._sa_handler +#define sa_sigaction _u._sa_sigaction + +#endif + +#endif diff --git a/tools/ioemu/patches/acpi-poweroff-support b/tools/ioemu/patches/acpi-poweroff-support new file mode 100644 index 0000000000..73dd7bb50f --- /dev/null +++ b/tools/ioemu/patches/acpi-poweroff-support @@ -0,0 +1,47 @@ +Index: ioemu/hw/piix4acpi.c +=================================================================== +--- ioemu.orig/hw/piix4acpi.c 2006-07-12 11:35:03.574468946 +0100 ++++ ioemu/hw/piix4acpi.c 2006-07-12 11:35:03.631461806 +0100 +@@ -45,6 +45,10 @@ + #define GBL_RLS (1 << 2) + #define SLP_EN (1 << 13) + ++/* Bits of PM1a register define here */ ++#define SLP_TYP_MASK 0x1C00 ++#define SLP_VAL 0x1C00 ++ + typedef struct AcpiDeviceState AcpiDeviceState; + AcpiDeviceState *acpi_device_table; + +@@ -270,7 +274,14 @@ + s->pm1_control = (val<<8)||(s->pm1_control); + /* printf("acpiPm1ControlP1_writeb \n addr %x val:%x\n", addr, val); */ + +-} ++ // Check for power off request ++ ++ if (((val & SLP_EN) != 0) && ++ ((val & SLP_TYP_MASK) == SLP_VAL)) { ++ s->pm1_timer=0x0; //clear ACPI timer ++ qemu_system_shutdown_request(); ++ } ++} + + static uint32_t acpiPm1ControlP1_readb(void *opaque, uint32_t addr) + { +@@ -337,7 +348,14 @@ + s->pm1_control = val; + /* printf("acpiPm1Control_writew \n addr %x val:%x\n", addr, val); */ + +-} ++ // Check for power off request ++ ++ if (((val & SLP_EN) != 0) && ++ ((val & SLP_TYP_MASK) == SLP_VAL)) { ++ qemu_system_shutdown_request(); ++ } ++ ++} + + static uint32_t acpiPm1Control_readw(void *opaque, uint32_t addr) + { diff --git a/tools/ioemu/patches/acpi-support b/tools/ioemu/patches/acpi-support new file mode 100644 index 0000000000..bb48015ad5 --- /dev/null +++ b/tools/ioemu/patches/acpi-support @@ -0,0 +1,476 @@ +diff -r 2612be97caad Makefile.target +--- a/Makefile.target Mon Jun 26 16:07:22 2006 +0100 ++++ b/Makefile.target Mon Jun 26 18:38:49 2006 +0100 +@@ -335,6 +335,7 @@ VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SO + VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) + VL_OBJS+= fdc.o mc146818rtc.o serial.o pc.o + VL_OBJS+= cirrus_vga.o mixeng.o parallel.o ++VL_OBJS+= piix4acpi.o + DEFINES += -DHAS_AUDIO + endif + ifeq ($(TARGET_BASE_ARCH), ppc) +diff -r 2612be97caad hw/pc.c +--- a/hw/pc.c Mon Jun 26 16:07:22 2006 +0100 ++++ b/hw/pc.c Mon Jun 26 18:38:49 2006 +0100 +@@ -566,6 +566,9 @@ static int parallel_io[MAX_PARALLEL_PORT + static int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc }; + static int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 }; + ++/* PIIX4 acpi pci configuration space, func 3 */ ++extern void pci_piix4_acpi_init(PCIBus *bus); ++ + #ifdef HAS_AUDIO + static void audio_init (PCIBus *pci_bus) + { +@@ -867,6 +870,10 @@ static void pc_init1(uint64_t ram_size, + + cmos_init(ram_size, boot_device, bs_table, timeoffset); + ++ /* using PIIX4 acpi model */ ++ if (pci_enabled) ++ pci_piix4_acpi_init(pci_bus); ++ + if (pci_enabled && usb_enabled) { + usb_uhci_init(pci_bus, usb_root_ports); + usb_attach(usb_root_ports[0], vm_usb_hub); +diff -r 2612be97caad hw/pci.c +--- a/hw/pci.c Mon Jun 26 16:07:22 2006 +0100 ++++ b/hw/pci.c Mon Jun 26 18:38:49 2006 +0100 +@@ -1697,7 +1697,7 @@ static uint32_t pci_bios_io_addr; + static uint32_t pci_bios_io_addr; + static uint32_t pci_bios_mem_addr; + /* host irqs corresponding to PCI irqs A-D */ +-static uint8_t pci_irqs[4] = { 11, 9, 11, 9 }; ++static uint8_t pci_irqs[4] = { 10, 11, 10, 11 }; + + static void pci_set_io_region_addr(PCIDevice *d, int region_num, uint32_t addr) + { +@@ -1750,12 +1750,22 @@ static void pci_bios_init_device(PCIDevi + pci_set_io_region_addr(d, 3, 0x374); + } + break; ++ case 0x0680: ++ if (vendor_id == 0x8086 && device_id == 0x7113) { ++ // PIIX4 ACPI PM ++ pci_config_writew(d, 0x20, 0x0000); // NO smb bus IO enable in PIIX4 ++ pci_config_writew(d, 0x22, 0x0000); ++ goto default_map; ++ } ++ break; ++ + case 0x0300: + if (vendor_id != 0x1234) + goto default_map; + /* VGA: map frame buffer to default Bochs VBE address */ + pci_set_io_region_addr(d, 0, 0xE0000000); + break; ++ + case 0x0800: + /* PIC */ + vendor_id = pci_config_readw(d, PCI_VENDOR_ID); +@@ -1800,6 +1810,13 @@ static void pci_bios_init_device(PCIDevi + pic_irq = pci_irqs[pin]; + pci_config_writeb(d, PCI_INTERRUPT_LINE, pic_irq); + } ++ if (class== 0x0680&& vendor_id == 0x8086 && device_id == 0x7113) { ++ // PIIX4 ACPI PM ++ pci_config_writew(d, 0x20, 0x0000); // NO smb bus IO enable in PIIX4 ++ pci_config_writew(d, 0x22, 0x0000); ++ pci_config_writew(d, 0x3c, 0x0009); // Hardcodeed IRQ9 ++ pci_config_writew(d, 0x3d, 0x0001); ++ } + } + + /* +diff -r 2612be97caad hw/piix4acpi.c +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/hw/piix4acpi.c Mon Jun 26 18:38:49 2006 +0100 +@@ -0,0 +1,388 @@ ++/* ++ * PIIX4 ACPI controller emulation ++ * ++ * Winston liwen Wang, winston.l.wang@intel.com ++ * Copyright (c) 2006 , Intel Corporation. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "vl.h" ++#define FREQUENCE_PMTIMER 3753425 ++/* acpi register bit define here */ ++ ++/* PM1_STS */ ++#define TMROF_STS (1 << 0) ++#define BM_STS (1 << 4) ++#define GBL_STS (1 << 5) ++#define PWRBTN_STS (1 << 8) ++#define RTC_STS (1 << 10) ++#define PRBTNOR_STS (1 << 11) ++#define WAK_STS (1 << 15) ++/* PM1_EN */ ++#define TMROF_EN (1 << 0) ++#define GBL_EN (1 << 5) ++#define PWRBTN_EN (1 << 8) ++#define RTC_EN (1 << 10) ++/* PM1_CNT */ ++#define SCI_EN (1 << 0) ++#define GBL_RLS (1 << 2) ++#define SLP_EN (1 << 13) ++ ++typedef struct AcpiDeviceState AcpiDeviceState; ++AcpiDeviceState *acpi_device_table; ++ ++/* Bits of PM1a register define here */ ++typedef struct PM1Event_BLK { ++ uint16_t pm1_status; /* pm1a_EVT_BLK */ ++ uint16_t pm1_enable; /* pm1a_EVT_BLK+2 */ ++}PM1Event_BLK; ++ ++typedef struct PCIAcpiState { ++ PCIDevice dev; ++ uint16_t irq; ++ uint16_t pm1_status; /* pm1a_EVT_BLK */ ++ uint16_t pm1_enable; /* pm1a_EVT_BLK+2 */ ++ uint16_t pm1_control; /* pm1a_ECNT_BLK */ ++ uint32_t pm1_timer; /* pmtmr_BLK */ ++} PCIAcpiState; ++ ++static PCIAcpiState *acpi_state; ++ ++static inline void acpi_set_irq(PCIAcpiState *s) ++{ ++/* no real SCI event need for now, so comment the following line out */ ++/* pic_set_irq(s->irq, 1); */ ++ printf("acpi_set_irq: s->irq %x \n",s->irq); ++} ++ ++static void acpi_reset(PCIAcpiState *s) ++{ ++ uint8_t *pci_conf; ++ pci_conf = s->dev.config; ++ ++ pci_conf[0x42] = 0x00; ++ pci_conf[0x43] = 0x00; ++ s->irq = 9; ++ s->pm1_status = 0; ++ s->pm1_enable = 0x00; /* TMROF_EN should cleared */ ++ s->pm1_control = SCI_EN; /* SCI_EN */ ++ s->pm1_timer = 0; ++} ++ ++/*byte access */ ++static void acpiPm1Status_writeb(void *opaque, uint32_t addr, uint32_t val) ++{ ++ PCIAcpiState *s = opaque; ++ ++ if ((val&TMROF_STS)==TMROF_STS) ++ s->pm1_status = s->pm1_status&!TMROF_STS; ++ ++ if ((val&GBL_STS)==GBL_STS) ++ s->pm1_status = s->pm1_status&!GBL_STS; ++ ++/* printf("acpiPm1Status_writeb \n addr %x val:%x pm1_status:%x \n", addr, val,s->pm1_status); */ ++} ++ ++static uint32_t acpiPm1Status_readb(void *opaque, uint32_t addr) ++{ ++ PCIAcpiState *s = opaque; ++ uint32_t val; ++ ++ val = s->pm1_status; ++/* printf("acpiPm1Status_readb \n addr %x val:%x\n", addr, val); */ ++ ++ return val; ++} ++ ++static void acpiPm1StatusP1_writeb(void *opaque, uint32_t addr, uint32_t val) ++{ ++ PCIAcpiState *s = opaque; ++ ++ s->pm1_status = (val<<8)||(s->pm1_status); ++/* printf("acpiPm1StatusP1_writeb \n addr %x val:%x\n", addr, val); */ ++} ++ ++static uint32_t acpiPm1StatusP1_readb(void *opaque, uint32_t addr) ++{ ++ PCIAcpiState *s = opaque; ++ uint32_t val; ++ ++ val = (s->pm1_status)>>8; ++ printf("acpiPm1StatusP1_readb \n addr %x val:%x\n", addr, val); ++ ++ return val; ++} ++ ++static void acpiPm1Enable_writeb(void *opaque, uint32_t addr, uint32_t val) ++{ ++ PCIAcpiState *s = opaque; ++ ++ s->pm1_enable = val; ++/* printf("acpiPm1Enable_writeb \n addr %x val:%x\n", addr, val); */ ++} ++ ++static uint32_t acpiPm1Enable_readb(void *opaque, uint32_t addr) ++{ ++ PCIAcpiState *s = opaque; ++ uint32_t val; ++ ++ val = (s->pm1_enable)||0x1; ++/* printf("acpiPm1Enable_readb \n addr %x val:%x\n", addr, val); */ ++ ++ return val; ++} ++ ++static void acpiPm1EnableP1_writeb(void *opaque, uint32_t addr, uint32_t val) ++{ ++ PCIAcpiState *s = opaque; ++ ++ s->pm1_enable = (val<<8)||(s->pm1_enable); ++/* printf("acpiPm1EnableP1_writeb \n addr %x val:%x\n", addr, val); */ ++ ++} ++ ++static uint32_t acpiPm1EnableP1_readb(void *opaque, uint32_t addr) ++{ ++ PCIAcpiState *s = opaque; ++ uint32_t val; ++ ++ val = (s->pm1_enable)>>8; ++/* printf("acpiPm1EnableP1_readb \n addr %x val:%x\n", addr, val); */ ++ ++ return val; ++} ++ ++static void acpiPm1Control_writeb(void *opaque, uint32_t addr, uint32_t val) ++{ ++ PCIAcpiState *s = opaque; ++ ++ s->pm1_control = val; ++/* printf("acpiPm1Control_writeb \n addr %x val:%x\n", addr, val); */ ++ ++} ++ ++static uint32_t acpiPm1Control_readb(void *opaque, uint32_t addr) ++{ ++ PCIAcpiState *s = opaque; ++ uint32_t val; ++ ++ val = s->pm1_control; ++/* printf("acpiPm1Control_readb \n addr %x val:%x\n", addr, val); */ ++ ++ return val; ++} ++ ++static void acpiPm1ControlP1_writeb(void *opaque, uint32_t addr, uint32_t val) ++{ ++ PCIAcpiState *s = opaque; ++ ++ s->pm1_control = (val<<8)||(s->pm1_control); ++/* printf("acpiPm1ControlP1_writeb \n addr %x val:%x\n", addr, val); */ ++ ++} ++ ++static uint32_t acpiPm1ControlP1_readb(void *opaque, uint32_t addr) ++{ ++ PCIAcpiState *s = opaque; ++ uint32_t val; ++ ++ val = (s->pm1_control)>>8; ++/* printf("acpiPm1ControlP1_readb \n addr %x val:%x\n", addr, val); */ ++ ++ return val; ++} ++ ++ ++/* word access */ ++ ++static void acpiPm1Status_writew(void *opaque, uint32_t addr, uint32_t val) ++{ ++ PCIAcpiState *s = opaque; ++ ++ if ((val&TMROF_STS)==TMROF_STS) ++ s->pm1_status = s->pm1_status&!TMROF_STS; ++ ++ if ((val&GBL_STS)==GBL_STS) ++ s->pm1_status = s->pm1_status&!GBL_STS; ++ ++/* printf("acpiPm1Status_writew \n addr %x val:%x pm1_status:%x \n", addr, val,s->pm1_status); */ ++} ++ ++static uint32_t acpiPm1Status_readw(void *opaque, uint32_t addr) ++{ ++ PCIAcpiState *s = opaque; ++ uint32_t val; ++ ++ val = s->pm1_status; ++/* printf("acpiPm1Status_readw \n addr %x val:%x\n", addr, val); */ ++ ++ return val; ++} ++ ++static void acpiPm1Enable_writew(void *opaque, uint32_t addr, uint32_t val) ++{ ++ PCIAcpiState *s = opaque; ++ ++ s->pm1_enable = val; ++/* printf("acpiPm1Enable_writew \n addr %x val:%x\n", addr, val); */ ++ ++} ++ ++static uint32_t acpiPm1Enable_readw(void *opaque, uint32_t addr) ++{ ++ PCIAcpiState *s = opaque; ++ uint32_t val; ++ ++ val = s->pm1_enable; ++/* printf("acpiPm1Enable_readw \n addr %x val:%x\n", addr, val); */ ++ ++ return val; ++} ++ ++static void acpiPm1Control_writew(void *opaque, uint32_t addr, uint32_t val) ++{ ++ PCIAcpiState *s = opaque; ++ ++ s->pm1_control = val; ++/* printf("acpiPm1Control_writew \n addr %x val:%x\n", addr, val); */ ++ ++} ++ ++static uint32_t acpiPm1Control_readw(void *opaque, uint32_t addr) ++{ ++ PCIAcpiState *s = opaque; ++ uint32_t val; ++ ++ val = s->pm1_control; ++/* printf("acpiPm1Control_readw \n addr %x val:%x\n", addr, val); */ ++ ++ return val; ++} ++ ++/* dword access */ ++ ++static void acpiPm1Event_writel(void *opaque, uint32_t addr, uint32_t val) ++{ ++ PCIAcpiState *s = opaque; ++ ++ s->pm1_status = val; ++ s->pm1_enable = val>>16; ++/* printf("acpiPm1Event_writel \n addr %x val:%x \n", addr, val); */ ++ ++} ++ ++static void acpiPm1Event_readl(void *opaque, uint32_t addr) ++{ ++ PCIAcpiState *s = opaque; ++ uint32_t val; ++ ++ val=s->pm1_status|(s->pm1_enable<<16); ++/* printf("acpiPm1Event_readl \n addr %x val:%x\n", addr, val); */ ++} ++ ++static void acpiPm1Timer_writel(void *opaque, uint32_t addr, uint32_t val) ++{ ++ PCIAcpiState *s = opaque; ++ ++ s->pm1_timer = val; ++/* printf("acpiPm1Timer_writel \n addr %x val:%x\n", addr, val); */ ++} ++ ++static uint32_t acpiPm1Timer_readl(void *opaque, uint32_t addr) ++{ ++ PCIAcpiState *s = opaque; ++ uint32_t val; ++ ++ val = s->pm1_timer; ++/* printf("acpiPm1Timer_readl \n addr %x val:%x\n", addr, val); */ ++ return val; ++} ++ ++static void acpi_map(PCIDevice *pci_dev, int region_num, ++ uint32_t addr, uint32_t size, int type) ++{ ++ PCIAcpiState *d = (PCIAcpiState *)pci_dev; ++ ++ printf("register acpi io \n"); ++ ++ /* Byte access */ ++ register_ioport_write(addr, 1, 1, acpiPm1Status_writeb, d); ++ register_ioport_read(addr, 1, 1, acpiPm1Status_readb, d); ++ register_ioport_write(addr+1, 1, 1, acpiPm1StatusP1_writeb, d); ++ register_ioport_read(addr+1, 1, 1, acpiPm1StatusP1_readb, d); ++ ++ register_ioport_write(addr + 2, 1, 1, acpiPm1Enable_writeb, d); ++ register_ioport_read(addr + 2, 1, 1, acpiPm1Enable_readb, d); ++ register_ioport_write(addr + 2 +1, 1, 1, acpiPm1EnableP1_writeb, d); ++ register_ioport_read(addr + 2 +1, 1, 1, acpiPm1EnableP1_readb, d); ++ ++ register_ioport_write(addr + 4, 1, 1, acpiPm1Control_writeb, d); ++ register_ioport_read(addr + 4, 1, 1, acpiPm1Control_readb, d); ++ register_ioport_write(addr + 4 + 1, 1, 1, acpiPm1ControlP1_writeb, d); ++ register_ioport_read(addr + 4 +1, 1, 1, acpiPm1ControlP1_readb, d); ++ ++ /* Word access */ ++ register_ioport_write(addr, 2, 2, acpiPm1Status_writew, d); ++ register_ioport_read(addr, 2, 2, acpiPm1Status_readw, d); ++ ++ register_ioport_write(addr + 2, 2, 2, acpiPm1Enable_writew, d); ++ register_ioport_read(addr + 2, 2, 2, acpiPm1Enable_readw, d); ++ ++ register_ioport_write(addr + 4, 2, 2, acpiPm1Control_writew, d); ++ register_ioport_read(addr + 4, 2, 2, acpiPm1Control_readw, d); ++ ++ /* DWord access */ ++ register_ioport_write(addr, 4, 4, acpiPm1Event_writel, d); ++ register_ioport_read(addr, 4, 4, acpiPm1Event_readl, d); ++ ++ register_ioport_write(addr + 8, 4, 4, acpiPm1Timer_writel, d); ++ register_ioport_read(addr + 8, 4, 4, acpiPm1Timer_readl, d); ++} ++ ++ ++/* PIIX4 acpi pci configuration space, func 3 */ ++void pci_piix4_acpi_init(PCIBus *bus) ++{ ++ PCIAcpiState *d; ++ uint8_t *pci_conf; ++ ++ /* register a function 3 of PIIX4 */ ++ d = (PCIAcpiState *)pci_register_device( ++ bus, "PIIX4 ACPI", sizeof(PCIAcpiState), ++ ((PCIDevice *)piix3_state)->devfn + 3, NULL, NULL); ++ ++ acpi_state = d; ++ pci_conf = d->dev.config; ++ pci_conf[0x00] = 0x86; /* Intel */ ++ pci_conf[0x01] = 0x80; ++ pci_conf[0x02] = 0x13; ++ pci_conf[0x03] = 0x71; ++ pci_conf[0x08] = 0x01; /* B0 stepping */ ++ pci_conf[0x09] = 0x00; /* base class */ ++ pci_conf[0x0a] = 0x80; /* Sub class */ ++ pci_conf[0x0b] = 0x06; ++ pci_conf[0x0e] = 0x00; ++ pci_conf[0x3d] = 0x01; /* Hardwired to PIRQA is used */ ++ ++ pci_register_io_region((PCIDevice *)d, 4, 0x10, ++ PCI_ADDRESS_SPACE_IO, acpi_map); ++ ++ acpi_reset (d); ++} diff --git a/tools/ioemu/patches/acpi-timer-support b/tools/ioemu/patches/acpi-timer-support new file mode 100644 index 0000000000..b97dcd06b9 --- /dev/null +++ b/tools/ioemu/patches/acpi-timer-support @@ -0,0 +1,128 @@ +diff -r 45c8b171d210 hw/piix4acpi.c +--- a/hw/piix4acpi.c Wed Jun 28 20:33:30 2006 +0100 ++++ b/hw/piix4acpi.c Thu Jun 29 09:34:35 2006 +0100 +@@ -49,6 +49,13 @@ AcpiDeviceState *acpi_device_table; + AcpiDeviceState *acpi_device_table; + + /* Bits of PM1a register define here */ ++typedef struct PMTState { ++ uint32_t count; ++ int irq; ++ uint64_t next_pm_time; ++ QEMUTimer *pm_timer; ++}PMTState; ++ + typedef struct PM1Event_BLK { + uint16_t pm1_status; /* pm1a_EVT_BLK */ + uint16_t pm1_enable; /* pm1a_EVT_BLK+2 */ +@@ -63,13 +70,80 @@ typedef struct PCIAcpiState { + uint32_t pm1_timer; /* pmtmr_BLK */ + } PCIAcpiState; + ++static PMTState *pmtimer_state; + static PCIAcpiState *acpi_state; ++ ++static void pmtimer_save(QEMUFile *f, void *opaque) ++{ ++ PMTState *s = opaque; ++ ++ qemu_put_be32s(f, &s->count); ++ qemu_put_be32s(f, &s->irq); ++ qemu_put_be64s(f, &s->next_pm_time); ++ qemu_put_timer(f, s->pm_timer); ++} ++ ++static int pmtimer_load(QEMUFile *f, void *opaque, int version_id) ++{ ++ PMTState *s = opaque; ++ ++ if (version_id != 1) ++ return -EINVAL; ++ qemu_get_be32s(f, &s->count); ++ qemu_get_be32s(f, &s->irq); ++ qemu_get_be64s(f, &s->next_pm_time); ++ qemu_get_timer(f, s->pm_timer); ++ return 0; ++} + + static inline void acpi_set_irq(PCIAcpiState *s) + { + /* no real SCI event need for now, so comment the following line out */ + /* pic_set_irq(s->irq, 1); */ + printf("acpi_set_irq: s->irq %x \n",s->irq); ++} ++ ++static void pm_timer_update(void *opaque) ++{ ++ PMTState *s = opaque; ++ s->next_pm_time = qemu_get_clock(vm_clock) + ++ muldiv64(1, ticks_per_sec,FREQUENCE_PMTIMER); ++ qemu_mod_timer(s->pm_timer, s->next_pm_time); ++ acpi_state->pm1_timer ++; ++ ++ /* If pm timer is zero then reset it to zero. */ ++ if (acpi_state->pm1_timer >= 0x1000000) { ++/* printf("pm_timerupdate: timer overflow: %x \n", acpi_state->pm1_timer); */ ++ ++ acpi_state->pm1_timer = 0; ++ acpi_state->pm1_status = acpi_state->pm1_status | TMROF_STS; ++ /* If TMROF_EN is set then send the irq. */ ++ if ((acpi_state->pm1_enable & TMROF_EN) == TMROF_EN) { ++ acpi_set_irq(acpi_state); ++ acpi_state->pm1_enable = 0x00; /* only need one time...*/ ++ } ++ } ++ s->count = acpi_state->pm1_timer; ++} ++ ++static PMTState *pmtimer_init(void) ++{ ++ PMTState *s; ++ ++ s = qemu_mallocz(sizeof(PMTState)); ++ if (!s) ++ return NULL; ++ ++ /* s->irq = irq; */ ++ ++ s->pm_timer = qemu_new_timer(vm_clock, pm_timer_update, s); ++ ++ s->count = 0; ++ s->next_pm_time = qemu_get_clock(vm_clock) + muldiv64(1, ticks_per_sec,FREQUENCE_PMTIMER) + 1; ++ qemu_mod_timer(s->pm_timer, s->next_pm_time); ++ ++ register_savevm("pm timer", 1, 1, pmtimer_save, pmtimer_load, s); ++ return s; + } + + static void acpi_reset(PCIAcpiState *s) +@@ -288,13 +362,15 @@ static void acpiPm1Event_writel(void *op + + } + +-static void acpiPm1Event_readl(void *opaque, uint32_t addr) +-{ +- PCIAcpiState *s = opaque; +- uint32_t val; +- +- val=s->pm1_status|(s->pm1_enable<<16); ++static uint32_t acpiPm1Event_readl(void *opaque, uint32_t addr) ++{ ++ PCIAcpiState *s = opaque; ++ uint32_t val; ++ ++ val = s->pm1_status|(s->pm1_enable<<16); + /* printf("acpiPm1Event_readl \n addr %x val:%x\n", addr, val); */ ++ ++ return val; + } + + static void acpiPm1Timer_writel(void *opaque, uint32_t addr, uint32_t val) +@@ -384,5 +460,7 @@ void pci_piix4_acpi_init(PCIBus *bus) + pci_register_io_region((PCIDevice *)d, 4, 0x10, + PCI_ADDRESS_SPACE_IO, acpi_map); + ++ pmtimer_state = pmtimer_init(); ++ + acpi_reset (d); + } diff --git a/tools/ioemu/patches/domain-destroy b/tools/ioemu/patches/domain-destroy new file mode 100644 index 0000000000..033ee1bc52 --- /dev/null +++ b/tools/ioemu/patches/domain-destroy @@ -0,0 +1,51 @@ +diff -r e1dce5114e92 monitor.c +--- a/monitor.c Mon Jun 26 16:04:15 2006 +0100 ++++ b/monitor.c Mon Jun 26 16:04:26 2006 +0100 +@@ -306,6 +306,7 @@ static void do_info_history (void) + + static void do_quit(void) + { ++ destroy_hvm_domain(); + exit(0); + } + +diff -r e1dce5114e92 target-i386-dm/helper2.c +--- a/target-i386-dm/helper2.c Mon Jun 26 16:04:15 2006 +0100 ++++ b/target-i386-dm/helper2.c Mon Jun 26 16:04:26 2006 +0100 +@@ -483,5 +483,25 @@ int main_loop(void) + shared_page->vcpu_iodata[send_vcpu].dm_eport); + } + } ++ destroy_hvm_domain(); + return 0; + } ++ ++void destroy_hvm_domain(void) ++{ ++ int xcHandle; ++ int sts; ++ ++ xcHandle = xc_interface_open(); ++ if (xcHandle < 0) ++ fprintf(logfile, "Cannot acquire xenctrl handle\n"); ++ else { ++ sts = xc_domain_shutdown(xcHandle, domid, SHUTDOWN_poweroff); ++ if (sts != 0) ++ fprintf(logfile, "? xc_domain_shutdown failed to issue poweroff, " ++ "sts %d, errno %d\n", sts, errno); ++ else ++ fprintf(logfile, "Issued domain %d poweroff\n", domid); ++ xc_interface_close(xcHandle); ++ } ++} +diff -r e1dce5114e92 vl.h +--- a/vl.h Mon Jun 26 16:04:15 2006 +0100 ++++ b/vl.h Mon Jun 26 16:04:26 2006 +0100 +@@ -1098,4 +1098,7 @@ void kqemu_record_dump(void); + void kqemu_record_dump(void); + + extern char domain_name[]; ++ ++void destroy_hvm_domain(void); ++ + #endif /* VL_H */ diff --git a/tools/ioemu/patches/domain-reset b/tools/ioemu/patches/domain-reset new file mode 100644 index 0000000000..d7a3dd09df --- /dev/null +++ b/tools/ioemu/patches/domain-reset @@ -0,0 +1,75 @@ +Index: ioemu/target-i386-dm/helper2.c +=================================================================== +--- ioemu.orig/target-i386-dm/helper2.c 2006-07-12 11:35:00.710827712 +0100 ++++ ioemu/target-i386-dm/helper2.c 2006-07-12 11:35:02.419613627 +0100 +@@ -123,6 +123,25 @@ + /* called from main_cpu_reset */ + void cpu_reset(CPUX86State *env) + { ++ int xcHandle; ++ int sts; ++ ++ /* pause domain first, to avoid repeated reboot request*/ ++ xc_domain_pause(xc_handle, domid); ++ ++ xcHandle = xc_interface_open(); ++ if (xcHandle < 0) ++ fprintf(logfile, "Cannot acquire xenctrl handle\n"); ++ else { ++ sts = xc_domain_shutdown(xcHandle, domid, SHUTDOWN_reboot); ++ if (sts != 0) ++ fprintf(logfile, ++ "? xc_domain_shutdown failed to issue reboot, sts %d\n", ++ sts); ++ else ++ fprintf(logfile, "Issued domain %d reboot\n", domid); ++ xc_interface_close(xcHandle); ++ } + } + + void cpu_x86_close(CPUX86State *env) +@@ -449,6 +468,10 @@ + if (vm_running) { + if (shutdown_requested) + break; ++ if (reset_requested) { ++ qemu_system_reset(); ++ reset_requested = 0; ++ } + } + + /* Wait up to 10 msec. */ +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-07-12 11:35:02.273631916 +0100 ++++ ioemu/vl.c 2006-07-12 11:35:02.421613376 +0100 +@@ -4411,7 +4411,7 @@ + } QEMUResetEntry; + + static QEMUResetEntry *first_reset_entry; +-static int reset_requested; ++int reset_requested; + int shutdown_requested; + static int powerdown_requested; + +Index: ioemu/vl.h +=================================================================== +--- ioemu.orig/vl.h 2006-07-12 11:35:01.454734511 +0100 ++++ ioemu/vl.h 2006-07-12 11:35:02.422613251 +0100 +@@ -122,6 +122,7 @@ + + void qemu_register_reset(QEMUResetHandler *func, void *opaque); + void qemu_system_reset_request(void); ++void qemu_system_reset(void); + void qemu_system_shutdown_request(void); + void qemu_system_powerdown_request(void); + #if !defined(TARGET_SPARC) +@@ -131,6 +132,8 @@ + void qemu_system_powerdown(void); + #endif + ++extern int reset_requested; ++ + void main_loop_wait(int timeout); + + extern FILE *logfile; diff --git a/tools/ioemu/patches/domain-timeoffset b/tools/ioemu/patches/domain-timeoffset new file mode 100644 index 0000000000..52abd88d01 --- /dev/null +++ b/tools/ioemu/patches/domain-timeoffset @@ -0,0 +1,187 @@ +Index: ioemu/hw/mc146818rtc.c +=================================================================== +--- ioemu.orig/hw/mc146818rtc.c 2006-07-12 11:33:54.096180895 +0100 ++++ ioemu/hw/mc146818rtc.c 2006-07-12 11:35:03.205515168 +0100 +@@ -178,10 +178,27 @@ + } + } + ++static void send_timeoffset_msg(time_t delta) ++{ ++ ++/* This routine is used to inform another entity that the ++ base time offset has changed. For instance, if you ++ were using xenstore, you might want to write to the store ++ at this point. Or, you might use some other method. ++ Whatever you might choose, here's a hook point to implement it. ++ ++ One item of note is that this delta is in addition to ++ any existing offset you might be already using. */ ++ ++ return; ++} ++ + static void rtc_set_time(RTCState *s) + { + struct tm *tm = &s->current_tm; +- ++ time_t before, after; ++ ++ before = mktime(tm); + tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]); + tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]); + tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f); +@@ -193,6 +210,12 @@ + tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]); + tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1; + tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100; ++ ++ /* Compute, and send, the additional time delta ++ We could compute the total time delta, but this is ++ sufficient, and simple. */ ++ after = mktime(tm); ++ send_timeoffset_msg(after-before); + } + + static void rtc_copy_date(RTCState *s) +Index: ioemu/hw/pc.c +=================================================================== +--- ioemu.orig/hw/pc.c 2006-07-12 11:35:02.876556380 +0100 ++++ ioemu/hw/pc.c 2006-07-12 11:35:03.205515168 +0100 +@@ -151,7 +151,7 @@ + } + + /* hd_table must contain 4 block drivers */ +-static void cmos_init(uint64_t ram_size, int boot_device, BlockDriverState **hd_table) ++static void cmos_init(uint64_t ram_size, int boot_device, BlockDriverState **hd_table, time_t timeoffset) + { + RTCState *s = rtc_state; + int val; +@@ -162,6 +162,7 @@ + + /* set the CMOS date */ + time(&ti); ++ ti += timeoffset; + if (rtc_utc) + tm = gmtime(&ti); + else +@@ -613,7 +614,7 @@ + static void pc_init1(uint64_t ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, +- const char *initrd_filename, ++ const char *initrd_filename, time_t timeoffset, + int pci_enabled) + { + #ifndef NOBIOS +@@ -864,7 +865,7 @@ + + floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table); + +- cmos_init(ram_size, boot_device, bs_table); ++ cmos_init(ram_size, boot_device, bs_table, timeoffset); + + if (pci_enabled && usb_enabled) { + usb_uhci_init(pci_bus, usb_root_ports); +@@ -883,12 +884,13 @@ + int snapshot, + const char *kernel_filename, + const char *kernel_cmdline, +- const char *initrd_filename) ++ const char *initrd_filename, ++ time_t timeoffset) + { + pc_init1(ram_size, vga_ram_size, boot_device, + ds, fd_filename, snapshot, + kernel_filename, kernel_cmdline, +- initrd_filename, 1); ++ initrd_filename, timeoffset, 1); + } + + static void pc_init_isa(uint64_t ram_size, int vga_ram_size, int boot_device, +@@ -896,12 +898,13 @@ + int snapshot, + const char *kernel_filename, + const char *kernel_cmdline, +- const char *initrd_filename) ++ const char *initrd_filename, ++ time_t timeoffset) + { + pc_init1(ram_size, vga_ram_size, boot_device, + ds, fd_filename, snapshot, + kernel_filename, kernel_cmdline, +- initrd_filename, 0); ++ initrd_filename, timeoffset, 0); + } + + QEMUMachine pc_machine = { +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-07-12 11:35:03.004540346 +0100 ++++ ioemu/vl.c 2006-07-12 11:35:03.208514792 +0100 +@@ -164,6 +164,8 @@ + + int xc_handle; + ++time_t timeoffset = 0; ++ + char domain_name[1024] = { 'H','V', 'M', 'X', 'E', 'N', '-'}; + extern int domid; + +@@ -4799,6 +4801,7 @@ + #endif + "-loadvm file start right away with a saved state (loadvm in monitor)\n" + "-vnc display start a VNC server on display\n" ++ "-timeoffset time offset (in seconds) from local time\n" + "\n" + "During emulation, the following keys are useful:\n" + "ctrl-alt-f toggle full screen\n" +@@ -4889,6 +4892,7 @@ + + QEMU_OPTION_d, + QEMU_OPTION_vcpus, ++ QEMU_OPTION_timeoffset, + }; + + typedef struct QEMUOption { +@@ -4967,6 +4971,7 @@ + + { "d", HAS_ARG, QEMU_OPTION_d }, + { "vcpus", 1, QEMU_OPTION_vcpus }, ++ { "timeoffset", HAS_ARG, QEMU_OPTION_timeoffset }, + { NULL }, + }; + +@@ -5669,6 +5674,9 @@ + vcpus = atoi(optarg); + fprintf(logfile, "qemu: the number of cpus is %d\n", vcpus); + break; ++ case QEMU_OPTION_timeoffset: ++ timeoffset = strtol(optarg, NULL, 0); ++ break; + } + } + } +@@ -5961,7 +5969,8 @@ + + machine->init(ram_size, vga_ram_size, boot_device, + ds, fd_filename, snapshot, +- kernel_filename, kernel_cmdline, initrd_filename); ++ kernel_filename, kernel_cmdline, initrd_filename, ++ timeoffset); + + gui_timer = qemu_new_timer(rt_clock, gui_update, NULL); + qemu_mod_timer(gui_timer, qemu_get_clock(rt_clock)); +Index: ioemu/vl.h +=================================================================== +--- ioemu.orig/vl.h 2006-07-12 11:35:02.881555754 +0100 ++++ ioemu/vl.h 2006-07-12 11:35:03.209514667 +0100 +@@ -556,7 +556,7 @@ + int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, +- const char *initrd_filename); ++ const char *initrd_filename, time_t timeoffset); + + typedef struct QEMUMachine { + const char *name; diff --git a/tools/ioemu/patches/hypervisor-pit b/tools/ioemu/patches/hypervisor-pit new file mode 100644 index 0000000000..5b09f4b903 --- /dev/null +++ b/tools/ioemu/patches/hypervisor-pit @@ -0,0 +1,58 @@ +Index: ioemu/Makefile.target +=================================================================== +--- ioemu.orig/Makefile.target 2006-07-12 11:35:01.899678766 +0100 ++++ ioemu/Makefile.target 2006-07-12 11:35:02.711577049 +0100 +@@ -333,7 +333,7 @@ + ifeq ($(TARGET_BASE_ARCH), i386) + # Hardware support + VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) +-VL_OBJS+= fdc.o mc146818rtc.o serial.o i8254.o pcspk.o pc.o ++VL_OBJS+= fdc.o mc146818rtc.o serial.o pc.o + VL_OBJS+= cirrus_vga.o mixeng.o parallel.o + DEFINES += -DHAS_AUDIO + endif +Index: ioemu/hw/pc.c +=================================================================== +--- ioemu.orig/hw/pc.c 2006-07-12 11:35:02.059658723 +0100 ++++ ioemu/hw/pc.c 2006-07-12 11:35:02.712576924 +0100 +@@ -38,7 +38,9 @@ + + static fdctrl_t *floppy_controller; + static RTCState *rtc_state; ++#ifndef CONFIG_DM + static PITState *pit; ++#endif /* !CONFIG_DM */ + #ifndef CONFIG_DM + static IOAPICState *ioapic; + #endif /* !CONFIG_DM */ +@@ -803,8 +805,10 @@ + } + #endif /* !CONFIG_DM */ + isa_pic = pic_init(pic_irq_request, first_cpu); ++#ifndef CONFIG_DM + pit = pit_init(0x40, 0); + pcspk_init(pit); ++#endif /* !CONFIG_DM */ + #ifndef CONFIG_DM + if (pci_enabled) { + pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic); +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-07-12 11:35:02.649584815 +0100 ++++ ioemu/vl.c 2006-07-12 11:35:02.715576548 +0100 +@@ -5033,6 +5033,7 @@ + + #ifdef HAS_AUDIO + struct soundhw soundhw[] = { ++#ifndef CONFIG_DM + #ifdef TARGET_I386 + { + "pcspk", +@@ -5042,6 +5043,7 @@ + { .init_isa = pcspk_audio_init } + }, + #endif ++#endif /* !CONFIG_DM */ + { + "sb16", + "Creative Sound Blaster 16", diff --git a/tools/ioemu/patches/ide-hd-multithread b/tools/ioemu/patches/ide-hd-multithread new file mode 100644 index 0000000000..172fa146cc --- /dev/null +++ b/tools/ioemu/patches/ide-hd-multithread @@ -0,0 +1,155 @@ +diff -r d711aa4fa261 hw/ide.c +--- a/hw/ide.c Fri Jun 16 10:13:37 2006 +0200 ++++ b/hw/ide.c Fri Jun 16 10:30:28 2006 +0200 +@@ -22,6 +22,7 @@ + * THE SOFTWARE. + */ + #include "vl.h" ++#include + + /* debug IDE devices */ + //#define DEBUG_IDE +@@ -390,6 +391,48 @@ typedef struct PCIIDEState { + int type; /* see IDE_TYPE_xxx */ + } PCIIDEState; + ++#define DMA_MULTI_THREAD ++ ++#ifdef DMA_MULTI_THREAD ++ ++static int file_pipes[2]; ++ ++static void ide_dma_loop(BMDMAState *bm); ++static void dma_thread_loop(BMDMAState *bm); ++ ++static void *dma_thread_func(void* opaque) ++{ ++ BMDMAState* req; ++ ++ while (read(file_pipes[0], &req, sizeof(req))) { ++ dma_thread_loop(req); ++ } ++ ++ return NULL; ++} ++ ++static void dma_create_thread(void) ++{ ++ pthread_t tid; ++ int rt; ++ ++ if (pipe(file_pipes) != 0) { ++ fprintf(stderr, "create pipe failed\n"); ++ exit(1); ++ } ++ ++ if ((rt = pthread_create(&tid, NULL, dma_thread_func, NULL))) { ++ fprintf(stderr, "Oops, dma thread creation failed, errno=%d\n", rt); ++ exit(1); ++ } ++ ++ if ((rt = pthread_detach(tid))) { ++ fprintf(stderr, "Oops, dma thread detachment failed, errno=%d\n", rt); ++ exit(1); ++ } ++} ++#endif /* DMA_MULTI_THREAD */ ++ + static void ide_dma_start(IDEState *s, IDEDMAFunc *dma_cb); + + static void padstr(char *str, const char *src, int len) +@@ -695,7 +738,9 @@ static int ide_read_dma_cb(IDEState *s, + } + if (s->io_buffer_index >= s->io_buffer_size && s->nsector == 0) { + s->status = READY_STAT | SEEK_STAT; ++#ifndef DMA_MULTI_THREAD + ide_set_irq(s); ++#endif /* !DMA_MULTI_THREAD */ + #ifdef DEBUG_IDE_ATAPI + printf("dma status=0x%x\n", s->status); + #endif +@@ -795,7 +840,11 @@ static int ide_write_dma_cb(IDEState *s, + qemu_get_clock(vm_clock) + (ticks_per_sec / 1000)); + } else + #endif ++#ifndef DMA_MULTI_THREAD + ide_set_irq(s); ++#else /* !DMA_MULTI_THREAD */ ++ ; ++#endif /* DMA_MULTI_THREAD */ + return 0; + } + if (n > MAX_MULT_SECTORS) +@@ -1046,7 +1095,9 @@ static int ide_atapi_cmd_read_dma_cb(IDE + if (s->packet_transfer_size <= 0) { + s->status = READY_STAT; + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; ++#ifndef DMA_MULTI_THREAD + ide_set_irq(s); ++#endif /* !DMA_MULTI_THREAD */ + #ifdef DEBUG_IDE_ATAPI + printf("dma status=0x%x\n", s->status); + #endif +@@ -2219,9 +2270,30 @@ static void ide_map(PCIDevice *pci_dev, + } + } + ++static void ide_dma_finish(BMDMAState *bm) ++{ ++ IDEState *s = bm->ide_if; ++ ++ bm->status &= ~BM_STATUS_DMAING; ++ bm->status |= BM_STATUS_INT; ++ bm->dma_cb = NULL; ++ bm->ide_if = NULL; ++#ifdef DMA_MULTI_THREAD ++ ide_set_irq(s); ++#endif /* DMA_MULTI_THREAD */ ++} ++ + /* XXX: full callback usage to prepare non blocking I/Os support - + error handling */ ++#ifdef DMA_MULTI_THREAD + static void ide_dma_loop(BMDMAState *bm) ++{ ++ write(file_pipes[1], &bm, sizeof(bm)); ++} ++static void dma_thread_loop(BMDMAState *bm) ++#else /* DMA_MULTI_THREAD */ ++static void ide_dma_loop(BMDMAState *bm) ++#endif /* !DMA_MULTI_THREAD */ + { + struct { + uint32_t addr; +@@ -2257,10 +2329,7 @@ static void ide_dma_loop(BMDMAState *bm) + } + /* end of transfer */ + the_end: +- bm->status &= ~BM_STATUS_DMAING; +- bm->status |= BM_STATUS_INT; +- bm->dma_cb = NULL; +- bm->ide_if = NULL; ++ ide_dma_finish(bm); + } + + static void ide_dma_start(IDEState *s, IDEDMAFunc *dma_cb) +@@ -2486,6 +2555,9 @@ void pci_cmd646_ide_init(PCIBus *bus, Bl + cmd646_set_irq, d, 0); + ide_init2(&d->ide_if[2], hd_table[2], hd_table[3], + cmd646_set_irq, d, 1); ++#ifdef DMA_MULTI_THREAD ++ dma_create_thread(); ++#endif /* DMA_MULTI_THREAD */ + } + + /* hd_table must contain 4 block drivers */ +@@ -2521,6 +2593,9 @@ void pci_piix3_ide_init(PCIBus *bus, Blo + pic_set_irq_new, isa_pic, 15); + ide_init_ioport(&d->ide_if[0], 0x1f0, 0x3f6); + ide_init_ioport(&d->ide_if[2], 0x170, 0x376); ++#ifdef DMA_MULTI_THREAD ++ dma_create_thread(); ++#endif //DMA_MULTI_THREAD + } + + /***********************************************************/ diff --git a/tools/ioemu/patches/qemu-64bit b/tools/ioemu/patches/qemu-64bit new file mode 100644 index 0000000000..e8cee7de97 --- /dev/null +++ b/tools/ioemu/patches/qemu-64bit @@ -0,0 +1,93 @@ +diff -r 2b3e57b3e1ec cpu-all.h +--- a/cpu-all.h Mon Jun 26 15:16:39 2006 +0100 ++++ b/cpu-all.h Mon Jun 26 15:16:44 2006 +0100 +@@ -822,7 +822,7 @@ int cpu_inl(CPUState *env, int addr); + + /* memory API */ + +-extern int phys_ram_size; ++extern uint64_t phys_ram_size; + extern int phys_ram_fd; + extern uint8_t *phys_ram_base; + extern uint8_t *phys_ram_dirty; +diff -r 2b3e57b3e1ec hw/pc.c +--- a/hw/pc.c Mon Jun 26 15:16:39 2006 +0100 ++++ b/hw/pc.c Mon Jun 26 15:16:44 2006 +0100 +@@ -147,7 +147,7 @@ static void cmos_init_hd(int type_ofs, i + } + + /* hd_table must contain 4 block drivers */ +-static void cmos_init(int ram_size, int boot_device, BlockDriverState **hd_table) ++static void cmos_init(uint64_t ram_size, int boot_device, BlockDriverState **hd_table) + { + RTCState *s = rtc_state; + int val; +@@ -604,7 +604,7 @@ static void pc_init_ne2k_isa(NICInfo *nd + } + + /* PC hardware initialisation */ +-static void pc_init1(int ram_size, int vga_ram_size, int boot_device, ++static void pc_init1(uint64_t ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, +@@ -853,7 +853,7 @@ static void pc_init1(int ram_size, int v + } + } + +-static void pc_init_pci(int ram_size, int vga_ram_size, int boot_device, ++static void pc_init_pci(uint64_t ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, + int snapshot, + const char *kernel_filename, +@@ -866,7 +866,7 @@ static void pc_init_pci(int ram_size, in + initrd_filename, 1); + } + +-static void pc_init_isa(int ram_size, int vga_ram_size, int boot_device, ++static void pc_init_isa(uint64_t ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, + int snapshot, + const char *kernel_filename, +diff -r 2b3e57b3e1ec vl.c +--- a/vl.c Mon Jun 26 15:16:39 2006 +0100 ++++ b/vl.c Mon Jun 26 15:16:44 2006 +0100 +@@ -123,7 +123,7 @@ const char* keyboard_layout = NULL; + const char* keyboard_layout = NULL; + int64_t ticks_per_sec; + int boot_device = 'c'; +-int ram_size; ++uint64_t ram_size; + int pit_min_timer_count = 0; + int nb_nics; + NICInfo nd_table[MAX_NICS]; +@@ -5320,7 +5320,7 @@ int main(int argc, char **argv) + help(); + break; + case QEMU_OPTION_m: +- ram_size = atoi(optarg) * 1024 * 1024; ++ ram_size = atol(optarg) * 1024 * 1024; + if (ram_size <= 0) + help(); + if (ram_size > PHYS_RAM_MAX_SIZE) { +diff -r 2b3e57b3e1ec vl.h +--- a/vl.h Mon Jun 26 15:16:39 2006 +0100 ++++ b/vl.h Mon Jun 26 15:16:44 2006 +0100 +@@ -138,7 +138,7 @@ extern int xc_handle; + extern int xc_handle; + extern int domid; + +-extern int ram_size; ++extern uint64_t ram_size; + extern int bios_size; + extern int rtc_utc; + extern int cirrus_vga_enabled; +@@ -542,7 +542,7 @@ int qcow_compress_cluster(BlockDriverSta + + #ifndef QEMU_TOOL + +-typedef void QEMUMachineInitFunc(int ram_size, int vga_ram_size, ++typedef void QEMUMachineInitFunc(uint64_t ram_size, int vga_ram_size, + int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, diff --git a/tools/ioemu/patches/qemu-bugfixes b/tools/ioemu/patches/qemu-bugfixes new file mode 100644 index 0000000000..5e90ed8400 --- /dev/null +++ b/tools/ioemu/patches/qemu-bugfixes @@ -0,0 +1,23 @@ +diff -r d76fb4ee3e48 console.c +--- a/console.c Mon Jun 26 15:16:44 2006 +0100 ++++ b/console.c Mon Jun 26 15:47:43 2006 +0100 +@@ -449,7 +449,7 @@ static void text_console_resize(TextCons + c++; + } + } +- free(s->cells); ++ qemu_free(s->cells); + s->cells = cells; + } + +diff -r d76fb4ee3e48 usb-linux.c +--- a/usb-linux.c Mon Jun 26 15:16:44 2006 +0100 ++++ b/usb-linux.c Mon Jun 26 15:47:43 2006 +0100 +@@ -26,6 +26,7 @@ + #if defined(__linux__) + #include + #include ++#define __user /* new versions of usbdevice_fs.h use this private attribute */ + #include + #include + diff --git a/tools/ioemu/patches/qemu-cleanup b/tools/ioemu/patches/qemu-cleanup new file mode 100644 index 0000000000..002d197995 --- /dev/null +++ b/tools/ioemu/patches/qemu-cleanup @@ -0,0 +1,83 @@ +Index: ioemu/hw/vga.c +=================================================================== +--- ioemu.orig/hw/vga.c 2006-07-12 11:33:54.853085901 +0100 ++++ ioemu/hw/vga.c 2006-07-12 11:35:00.780818943 +0100 +@@ -1563,7 +1563,9 @@ + static void vga_save(QEMUFile *f, void *opaque) + { + VGAState *s = opaque; ++#ifdef CONFIG_BOCHS_VBE + int i; ++#endif + + qemu_put_be32s(f, &s->latch); + qemu_put_8s(f, &s->sr_index); +@@ -1604,7 +1606,10 @@ + static int vga_load(QEMUFile *f, void *opaque, int version_id) + { + VGAState *s = opaque; +- int is_vbe, i; ++ int is_vbe; ++#ifdef CONFIG_BOCHS_VBE ++ int i; ++#endif + + if (version_id != 1) + return -EINVAL; +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-07-12 11:35:00.708827963 +0100 ++++ ioemu/vl.c 2006-07-12 11:35:00.783818568 +0100 +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + #include + #include + #ifdef _BSD +@@ -4777,7 +4778,9 @@ + QEMU_OPTION_d, + QEMU_OPTION_hdachs, + QEMU_OPTION_L, ++#ifdef USE_CODE_COPY + QEMU_OPTION_no_code_copy, ++#endif + QEMU_OPTION_k, + QEMU_OPTION_localtime, + QEMU_OPTION_cirrusvga, +@@ -4844,7 +4847,9 @@ + { "d", HAS_ARG, QEMU_OPTION_d }, + { "hdachs", HAS_ARG, QEMU_OPTION_hdachs }, + { "L", HAS_ARG, QEMU_OPTION_L }, ++#ifdef USE_CODE_COPY + { "no-code-copy", 0, QEMU_OPTION_no_code_copy }, ++#endif + #ifdef USE_KQEMU + { "no-kqemu", 0, QEMU_OPTION_no_kqemu }, + { "kernel-kqemu", 0, QEMU_OPTION_kernel_kqemu }, +@@ -5274,9 +5279,11 @@ + case QEMU_OPTION_fdb: + fd_filename[1] = optarg; + break; ++#ifdef USE_CODE_COPY + case QEMU_OPTION_no_code_copy: + code_copy_enabled = 0; + break; ++#endif + case QEMU_OPTION_net: + if (nb_net_clients >= MAX_NET_CLIENTS) { + fprintf(stderr, "qemu: too many network clients\n"); +Index: ioemu/vl.h +=================================================================== +--- ioemu.orig/vl.h 2006-07-12 11:35:00.709827838 +0100 ++++ ioemu/vl.h 2006-07-12 11:35:00.784818442 +0100 +@@ -892,7 +892,7 @@ + unsigned long vram_offset, int vram_size, int width, int height); + + /* slavio_intctl.c */ +-void *slavio_intctl_init(); ++void *slavio_intctl_init(void); + void slavio_intctl_set_cpu(void *opaque, unsigned int cpu, CPUState *env); + void slavio_pic_info(void *opaque); + void slavio_irq_info(void *opaque); diff --git a/tools/ioemu/patches/qemu-dm b/tools/ioemu/patches/qemu-dm new file mode 100644 index 0000000000..b681eedeac --- /dev/null +++ b/tools/ioemu/patches/qemu-dm @@ -0,0 +1,491 @@ +Index: ioemu/Makefile.target +=================================================================== +--- ioemu.orig/Makefile.target 2006-07-12 11:35:00.382868802 +0100 ++++ ioemu/Makefile.target 2006-07-12 11:35:00.452860033 +0100 +@@ -285,7 +285,7 @@ + endif + + # must use static linking to avoid leaving stuff in virtual address space +-VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o loader.o ++VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o + VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o + ifdef CONFIG_WIN32 + VL_OBJS+=tap-win32.o +Index: ioemu/configure +=================================================================== +--- ioemu.orig/configure 2006-07-12 11:35:00.383868677 +0100 ++++ ioemu/configure 2006-07-12 11:35:00.453859908 +0100 +@@ -75,8 +75,8 @@ + bigendian="no" + mingw32="no" + EXESUF="" +-gdbstub="yes" +-slirp="yes" ++gdbstub="no" ++slirp="no" + adlib="no" + oss="no" + dsound="no" +@@ -727,6 +727,8 @@ + if expr $target : '.*-softmmu' > /dev/null ; then + target_softmmu="yes" + fi ++#for support 256M guest ++target_softmmu="yes" + target_user_only="no" + if expr $target : '.*-user' > /dev/null ; then + target_user_only="yes" +Index: ioemu/cpu-all.h +=================================================================== +--- ioemu.orig/cpu-all.h 2006-07-12 11:33:54.968071470 +0100 ++++ ioemu/cpu-all.h 2006-07-12 11:35:00.453859908 +0100 +@@ -690,7 +690,9 @@ + void page_set_flags(target_ulong start, target_ulong end, int flags); + void page_unprotect_range(target_ulong data, target_ulong data_size); + ++#ifdef CONFIG_DM + #define SINGLE_CPU_DEFINES ++#endif + #ifdef SINGLE_CPU_DEFINES + + #if defined(TARGET_I386) +@@ -745,6 +747,12 @@ + + #endif + ++#else /* SINGLE_CPU_DEFINES */ ++ ++#define CPUState CPUX86State ++#define cpu_init cpu_x86_init ++int main_loop(void); ++ + #endif /* SINGLE_CPU_DEFINES */ + + void cpu_dump_state(CPUState *env, FILE *f, +Index: ioemu/disas.h +=================================================================== +--- ioemu.orig/disas.h 2006-07-12 11:33:54.968071470 +0100 ++++ ioemu/disas.h 2006-07-12 11:35:00.453859908 +0100 +@@ -1,6 +1,7 @@ + #ifndef _QEMU_DISAS_H + #define _QEMU_DISAS_H + ++#ifndef CONFIG_DM + /* Disassemble this for me please... (debugging). */ + void disas(FILE *out, void *code, unsigned long size); + void target_disas(FILE *out, target_ulong code, target_ulong size, int flags); +@@ -17,5 +18,6 @@ + const char *disas_strtab; + struct syminfo *next; + } *syminfos; ++#endif /* !CONFIG_DM */ + + #endif /* _QEMU_DISAS_H */ +Index: ioemu/exec-all.h +=================================================================== +--- ioemu.orig/exec-all.h 2006-07-12 11:33:54.968071470 +0100 ++++ ioemu/exec-all.h 2006-07-12 11:35:00.454859782 +0100 +@@ -509,7 +509,7 @@ + + extern int tb_invalidated_flag; + +-#if !defined(CONFIG_USER_ONLY) ++#if !defined(CONFIG_USER_ONLY) && !defined(CONFIG_DM) + + void tlb_fill(target_ulong addr, int is_write, int is_user, + void *retaddr); +@@ -536,7 +536,7 @@ + + #endif + +-#if defined(CONFIG_USER_ONLY) ++#if defined(CONFIG_USER_ONLY) || defined(CONFIG_DM) + static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr) + { + return addr; +Index: ioemu/hw/pc.c +=================================================================== +--- ioemu.orig/hw/pc.c 2006-07-12 11:33:54.967071596 +0100 ++++ ioemu/hw/pc.c 2006-07-12 11:35:00.455859657 +0100 +@@ -65,6 +65,7 @@ + return qemu_get_clock(vm_clock); + } + ++#ifndef CONFIG_DM + /* IRQ handling */ + int cpu_get_pic_interrupt(CPUState *env) + { +@@ -81,6 +82,7 @@ + intno = pic_read_irq(isa_pic); + return intno; + } ++#endif /* CONFIG_DM */ + + static void pic_irq_request(void *opaque, int level) + { +@@ -394,6 +396,7 @@ + + /*************************************************/ + ++#ifndef CONFIG_DM + static void putb(uint8_t **pp, int val) + { + uint8_t *q; +@@ -540,6 +543,7 @@ + float_pointer_struct[10] = + -mpf_checksum(float_pointer_struct, q - float_pointer_struct); + } ++#endif /* !CONFIG_DM */ + + + static const int ide_iobase[2] = { 0x1f0, 0x170 }; +@@ -619,12 +623,14 @@ + /* init CPUs */ + for(i = 0; i < smp_cpus; i++) { + env = cpu_init(); ++#ifndef CONFIG_DM + if (i != 0) + env->hflags |= HF_HALTED_MASK; + if (smp_cpus > 1) { + /* XXX: enable it in all cases */ + env->cpuid_features |= CPUID_APIC; + } ++#endif /* !CONFIG_DM */ + register_savevm("cpu", i, 3, cpu_save, cpu_load, env); + qemu_register_reset(main_cpu_reset, env); + if (pci_enabled) { +@@ -683,6 +689,7 @@ + + bochs_bios_init(); + ++#ifndef CONFIG_DM + if (linux_boot) { + uint8_t bootsect[512]; + uint8_t old_bootsect[512]; +@@ -738,6 +745,7 @@ + /* loader type */ + stw_raw(phys_ram_base + KERNEL_PARAMS_ADDR + 0x210, 0x01); + } ++#endif /* !CONFIG_DM */ + + if (pci_enabled) { + pci_bus = i440fx_init(); +@@ -776,9 +784,11 @@ + isa_pic = pic_init(pic_irq_request, first_cpu); + pit = pit_init(0x40, 0); + pcspk_init(pit); ++#ifndef CONFIG_DM + if (pci_enabled) { + pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic); + } ++#endif /* !CONFIG_DM */ + + for(i = 0; i < MAX_SERIAL_PORTS; i++) { + if (serial_hds[i]) { +Index: ioemu/hw/vga_int.h +=================================================================== +--- ioemu.orig/hw/vga_int.h 2006-07-12 11:33:54.967071596 +0100 ++++ ioemu/hw/vga_int.h 2006-07-12 11:35:00.455859657 +0100 +@@ -28,7 +28,7 @@ + #define ST01_DISP_ENABLE 0x01 + + /* bochs VBE support */ +-#define CONFIG_BOCHS_VBE ++//#define CONFIG_BOCHS_VBE + + #define VBE_DISPI_MAX_XRES 1024 + #define VBE_DISPI_MAX_YRES 768 +Index: ioemu/monitor.c +=================================================================== +--- ioemu.orig/monitor.c 2006-07-12 11:33:54.968071470 +0100 ++++ ioemu/monitor.c 2006-07-12 11:35:00.456859532 +0100 +@@ -68,6 +68,12 @@ + + void term_flush(void) + { ++#ifdef CONFIG_DM ++ if (term_outbuf_index > 0 && !monitor_hd) { ++ fwrite(term_outbuf, term_outbuf_index, 1, stderr); ++ term_outbuf_index = 0; ++ } ++#endif + if (term_outbuf_index > 0) { + qemu_chr_write(monitor_hd, term_outbuf, term_outbuf_index); + term_outbuf_index = 0; +@@ -104,6 +110,7 @@ + va_end(ap); + } + ++#ifndef CONFIG_DM + static int monitor_fprintf(FILE *stream, const char *fmt, ...) + { + va_list ap; +@@ -112,6 +119,7 @@ + va_end(ap); + return 0; + } ++#endif /* !CONFIG_DM */ + + static int compare_cmd(const char *name, const char *list) + { +@@ -225,6 +233,7 @@ + return mon_cpu; + } + ++#ifndef CONFIG_DM + static void do_info_registers(void) + { + CPUState *env; +@@ -278,6 +287,7 @@ + { + dump_exec_info(NULL, monitor_fprintf); + } ++#endif /* !CONFIG_DM */ + + static void do_info_history (void) + { +@@ -375,6 +385,7 @@ + cpu_set_log(mask); + } + ++#ifndef CONFIG_DM + static void do_savevm(const char *filename) + { + if (qemu_savevm(filename) < 0) +@@ -621,6 +632,7 @@ + #endif + term_printf("\n"); + } ++#endif /* !CONFIG_DM */ + + static void do_sum(uint32_t start, uint32_t size) + { +@@ -792,6 +804,7 @@ + } + } + ++#ifndef CONFIG_DM + static void do_ioport_read(int count, int format, int size, int addr, int has_index, int index) + { + uint32_t val; +@@ -952,6 +965,7 @@ + } + } + #endif ++#endif /* !CONFIG_DM */ + + static void do_info_kqemu(void) + { +@@ -1045,6 +1059,7 @@ + "filename", "save screen into PPM image 'filename'" }, + { "log", "s", do_log, + "item1[,...]", "activate logging of the specified items to '/tmp/qemu.log'" }, ++#ifndef CONFIG_DM + { "savevm", "F", do_savevm, + "filename", "save the whole virtual machine state to 'filename'" }, + { "loadvm", "F", do_loadvm, +@@ -1065,21 +1080,26 @@ + "/fmt expr", "print expression value (use $reg for CPU register access)", }, + { "i", "/ii.", do_ioport_read, + "/fmt addr", "I/O port read" }, ++#endif/* !CONFIG_DM */ + + { "sendkey", "s", do_send_key, + "keys", "send keys to the VM (e.g. 'sendkey ctrl-alt-f1')" }, ++#ifndef CONFIG_DM + { "system_reset", "", do_system_reset, + "", "reset the system" }, + { "system_powerdown", "", do_system_powerdown, + "", "send system power down event" }, ++#endif /* !CONFIG_DM */ + { "sum", "ii", do_sum, + "addr size", "compute the checksum of a memory region" }, + { "usb_add", "s", do_usb_add, + "device", "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')" }, + { "usb_del", "s", do_usb_del, + "device", "remove USB device 'bus.addr'" }, ++#ifndef CONFIG_DM + { "cpu", "i", do_cpu_set, + "index", "set the default CPU" }, ++#endif /* !CONFIG_DM */ + { NULL, NULL, }, + }; + +@@ -1090,10 +1110,12 @@ + "", "show the network state" }, + { "block", "", do_info_block, + "", "show the block devices" }, ++#ifndef CONFIG_DM + { "registers", "", do_info_registers, + "", "show the cpu registers" }, + { "cpus", "", do_info_cpus, + "", "show infos for each CPU" }, ++#endif /* !CONFIG_DM */ + { "history", "", do_info_history, + "", "show the command line history", }, + { "irq", "", irq_info, +@@ -1102,6 +1124,7 @@ + "", "show i8259 (PIC) state", }, + { "pci", "", pci_info, + "", "show PCI info", }, ++#ifndef CONFIG_DM + #if defined(TARGET_I386) + { "tlb", "", tlb_info, + "", "show virtual to physical memory mappings", }, +@@ -1110,6 +1133,7 @@ + #endif + { "jit", "", do_info_jit, + "", "show dynamic compiler info", }, ++#endif /* !CONFIG_DM */ + { "kqemu", "", do_info_kqemu, + "", "show kqemu information", }, + { "usb", "", usb_info, +@@ -1123,6 +1147,7 @@ + + /*******************************************************************/ + ++#ifndef CONFIG_DM + static const char *pch; + static jmp_buf expr_env; + +@@ -1663,6 +1688,7 @@ + *pp = pch; + return 0; + } ++#endif /* !CONFIG_DM */ + + static int get_str(char *buf, int buf_size, const char **pp) + { +@@ -1729,8 +1755,10 @@ + return 0; + } + ++#ifndef CONFIG_DM + static int default_fmt_format = 'x'; + static int default_fmt_size = 4; ++#endif /* !CONFIG_DM */ + + #define MAX_ARGS 16 + +@@ -1738,7 +1766,10 @@ + { + const char *p, *pstart, *typestr; + char *q; +- int c, nb_args, len, i, has_arg; ++ int c, nb_args, len, i; ++#ifndef CONFIG_DM ++ int has_arg; ++#endif /* !CONFIG_DM */ + term_cmd_t *cmd; + char cmdname[256]; + char buf[1024]; +@@ -1830,6 +1861,7 @@ + args[nb_args++] = str; + } + break; ++#ifndef CONFIG_DM + case '/': + { + int count, format, size; +@@ -1962,6 +1994,7 @@ + } + } + break; ++#endif /* !CONFIG_DM */ + case '-': + { + int has_option; +@@ -1988,6 +2021,11 @@ + args[nb_args++] = (void *)has_option; + } + break; ++#ifdef CONFIG_DM ++ /* TODO: add more commands we need here to support hvm device model */ ++ case '/': ++ case 'i': ++#endif /* CONFIG_DM */ + default: + bad_type: + term_printf("%s: unknown type '%c'\n", cmdname, c); +@@ -2035,6 +2073,7 @@ + return; + } + ++#ifndef CONFIG_DM + static void cmd_completion(const char *name, const char *list) + { + const char *p, *pstart; +@@ -2222,6 +2261,11 @@ + for(i = 0; i < nb_args; i++) + qemu_free(args[i]); + } ++#else ++void readline_find_completion(const char *cmdline) ++{ ++} ++#endif /* !CONFIG_DM */ + + static int term_can_read(void *opaque) + { +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-07-12 11:33:54.967071596 +0100 ++++ ioemu/vl.c 2006-07-12 11:35:00.459859156 +0100 +@@ -423,12 +423,15 @@ + void hw_error(const char *fmt, ...) + { + va_list ap; ++#ifndef CONFIG_DM + CPUState *env; ++#endif /* !CONFIG_DM */ + + va_start(ap, fmt); + fprintf(stderr, "qemu: hardware error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); ++#ifndef CONFIG_DM + for(env = first_cpu; env != NULL; env = env->next_cpu) { + fprintf(stderr, "CPU #%d:\n", env->cpu_index); + #ifdef TARGET_I386 +@@ -437,6 +440,7 @@ + cpu_dump_state(env, stderr, fprintf, 0); + #endif + } ++#endif /* !CONFIG_DM */ + va_end(ap); + abort(); + } +@@ -3800,6 +3804,7 @@ + return ret; + } + ++#ifndef CONFIG_DM + /***********************************************************/ + /* cpu save/restore */ + +@@ -4234,6 +4239,25 @@ + } + return 0; + } ++#else /* CONFIG_DM */ ++void cpu_save(QEMUFile *f, void *opaque) ++{ ++} ++ ++int cpu_load(QEMUFile *f, void *opaque, int version_id) ++{ ++ return 0; ++} ++ ++static void ram_save(QEMUFile *f, void *opaque) ++{ ++} ++ ++static int ram_load(QEMUFile *f, void *opaque, int version_id) ++{ ++ return 0; ++} ++#endif /* CONFIG_DM */ + + /***********************************************************/ + /* machine registration */ diff --git a/tools/ioemu/patches/qemu-hvm-banner b/tools/ioemu/patches/qemu-hvm-banner new file mode 100644 index 0000000000..f199eae97a --- /dev/null +++ b/tools/ioemu/patches/qemu-hvm-banner @@ -0,0 +1,22 @@ +Index: ioemu/monitor.c +=================================================================== +--- ioemu.orig/monitor.c 2006-07-12 11:35:00.705828339 +0100 ++++ ioemu/monitor.c 2006-07-12 11:35:01.307752925 +0100 +@@ -2293,15 +2293,14 @@ + + static void monitor_start_input(void) + { +- readline_start("(qemu) ", 0, monitor_handle_command1, NULL); ++ readline_start("(HVMXen) ", 0, monitor_handle_command1, NULL); + } + + void monitor_init(CharDriverState *hd, int show_banner) + { + monitor_hd = hd; + if (show_banner) { +- term_printf("QEMU %s monitor - type 'help' for more information\n", +- QEMU_VERSION); ++ term_printf("HVM device model. type 'q' to exit\n"); + } + qemu_chr_add_read_handler(hd, term_can_read, term_read, NULL); + monitor_start_input(); diff --git a/tools/ioemu/patches/qemu-infrastructure b/tools/ioemu/patches/qemu-infrastructure new file mode 100644 index 0000000000..1bd4d7e60e --- /dev/null +++ b/tools/ioemu/patches/qemu-infrastructure @@ -0,0 +1,27 @@ +diff -r ead4d7bbf711 cpu-all.h +--- a/cpu-all.h Tue May 30 13:32:07 2006 +0100 ++++ b/cpu-all.h Tue May 30 13:32:25 2006 +0100 +@@ -820,6 +820,23 @@ int cpu_inl(CPUState *env, int addr); + int cpu_inl(CPUState *env, int addr); + #endif + ++#if defined(__i386__) || defined(__x86_64__) ++static __inline__ void atomic_set_bit(long nr, volatile void *addr) ++{ ++ __asm__ __volatile__( ++ "lock ; bts %1,%0" ++ :"=m" (*(volatile long *)addr) ++ :"dIr" (nr)); ++} ++static __inline__ void atomic_clear_bit(long nr, volatile void *addr) ++{ ++ __asm__ __volatile__( ++ "lock ; btr %1,%0" ++ :"=m" (*(volatile long *)addr) ++ :"dIr" (nr)); ++} ++#endif ++ + /* memory API */ + + extern uint64_t phys_ram_size; diff --git a/tools/ioemu/patches/qemu-logging b/tools/ioemu/patches/qemu-logging new file mode 100644 index 0000000000..c11dbb6ae3 --- /dev/null +++ b/tools/ioemu/patches/qemu-logging @@ -0,0 +1,70 @@ +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-07-12 11:35:00.955797021 +0100 ++++ ioemu/vl.c 2006-07-12 11:35:01.094779608 +0100 +@@ -4697,7 +4697,7 @@ + "-S freeze CPU at startup (use 'c' to start execution)\n" + "-s wait gdb connection to port %d\n" + "-p port change gdb connection port\n" +- "-d item1,... output log to %s (use -d ? for a list of log items)\n" ++ "-l item1,... output log to %s (use -d ? for a list of log items)\n" + "-hdachs c,h,s[,t] force hard disk 0 physical geometry and the optional BIOS\n" + " translation (t=none or lba) (usually qemu can guess them)\n" + "-L path set the directory for the BIOS and VGA BIOS\n" +@@ -4775,7 +4775,7 @@ + QEMU_OPTION_S, + QEMU_OPTION_s, + QEMU_OPTION_p, +- QEMU_OPTION_d, ++ QEMU_OPTION_l, + QEMU_OPTION_hdachs, + QEMU_OPTION_L, + #ifdef USE_CODE_COPY +@@ -4844,7 +4844,7 @@ + { "S", 0, QEMU_OPTION_S }, + { "s", 0, QEMU_OPTION_s }, + { "p", HAS_ARG, QEMU_OPTION_p }, +- { "d", HAS_ARG, QEMU_OPTION_d }, ++ { "l", HAS_ARG, QEMU_OPTION_l }, + { "hdachs", HAS_ARG, QEMU_OPTION_hdachs }, + { "L", HAS_ARG, QEMU_OPTION_L }, + #ifdef USE_CODE_COPY +@@ -5095,6 +5095,8 @@ + char usb_devices[MAX_VM_USB_PORTS][128]; + int usb_devices_index; + ++ char qemu_dm_logfilename[64]; ++ + LIST_INIT (&vm_change_state_head); + #if !defined(CONFIG_SOFTMMU) + /* we never want that malloc() uses mmap() */ +@@ -5144,6 +5146,11 @@ + nb_nics = 0; + /* default mac address of the first network interface */ + ++ /* init debug */ ++ sprintf(qemu_dm_logfilename, "/var/log/qemu-dm.%d.log", getpid()); ++ cpu_set_log_filename(qemu_dm_logfilename); ++ cpu_set_log(0); ++ + optind = 1; + for(;;) { + if (optind >= argc) +@@ -5329,7 +5336,7 @@ + exit(1); + } + break; +- case QEMU_OPTION_d: ++ case QEMU_OPTION_l: + { + int mask; + CPULogItem *item; +@@ -5698,7 +5705,7 @@ + stk.ss_flags = 0; + + if (sigaltstack(&stk, NULL) < 0) { +- perror("sigaltstack"); ++ fprintf(logfile, "sigaltstack returned error %d\n", errno); + exit(1); + } + } diff --git a/tools/ioemu/patches/qemu-no-apic b/tools/ioemu/patches/qemu-no-apic new file mode 100644 index 0000000000..be3f7a588b --- /dev/null +++ b/tools/ioemu/patches/qemu-no-apic @@ -0,0 +1,51 @@ +Index: ioemu/Makefile.target +=================================================================== +--- ioemu.orig/Makefile.target 2006-07-12 11:35:00.704828464 +0100 ++++ ioemu/Makefile.target 2006-07-12 11:35:01.899678766 +0100 +@@ -334,7 +334,7 @@ + # Hardware support + VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) + VL_OBJS+= fdc.o mc146818rtc.o serial.o i8254.o pcspk.o pc.o +-VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o ++VL_OBJS+= cirrus_vga.o mixeng.o parallel.o + DEFINES += -DHAS_AUDIO + endif + ifeq ($(TARGET_BASE_ARCH), ppc) +Index: ioemu/hw/pc.c +=================================================================== +--- ioemu.orig/hw/pc.c 2006-07-12 11:35:01.685705573 +0100 ++++ ioemu/hw/pc.c 2006-07-12 11:35:01.900678640 +0100 +@@ -39,7 +39,9 @@ + static fdctrl_t *floppy_controller; + static RTCState *rtc_state; + static PITState *pit; ++#ifndef CONFIG_DM + static IOAPICState *ioapic; ++#endif /* !CONFIG_DM */ + static USBPort *usb_root_ports[2]; + + static void ioport80_write(void *opaque, uint32_t addr, uint32_t data) +@@ -633,9 +635,11 @@ + #endif /* !CONFIG_DM */ + register_savevm("cpu", i, 3, cpu_save, cpu_load, env); + qemu_register_reset(main_cpu_reset, env); ++#ifndef CONFIG_DM + if (pci_enabled) { + apic_init(env); + } ++#endif /* !CONFIG_DM */ + } + + /* allocate RAM */ +@@ -782,9 +786,11 @@ + register_ioport_read(0x92, 1, 1, ioport92_read, NULL); + register_ioport_write(0x92, 1, 1, ioport92_write, NULL); + ++#ifndef CONFIG_DM + if (pci_enabled) { + ioapic = ioapic_init(); + } ++#endif /* !CONFIG_DM */ + isa_pic = pic_init(pic_irq_request, first_cpu); + pit = pit_init(0x40, 0); + pcspk_init(pit); diff --git a/tools/ioemu/patches/qemu-nobios b/tools/ioemu/patches/qemu-nobios new file mode 100644 index 0000000000..8d9464deb7 --- /dev/null +++ b/tools/ioemu/patches/qemu-nobios @@ -0,0 +1,61 @@ +diff -r 77a889b0aef8 hw/pc.c +--- a/hw/pc.c Wed Jun 14 19:29:20 2006 +0200 ++++ b/hw/pc.c Wed Jun 14 19:29:35 2006 +0200 +@@ -605,6 +605,8 @@ static void pc_init_ne2k_isa(NICInfo *nd + nb_ne2k++; + } + ++#define NOBIOS 1 ++ + /* PC hardware initialisation */ + static void pc_init1(uint64_t ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, +@@ -612,10 +614,15 @@ static void pc_init1(uint64_t ram_size, + const char *initrd_filename, + int pci_enabled) + { ++#ifndef NOBIOS + char buf[1024]; +- int ret, linux_boot, initrd_size, i; ++ int ret, initrd_size; ++#endif ++ int linux_boot, i; ++#ifndef NOBIOS + unsigned long bios_offset, vga_bios_offset; + int bios_size, isa_bios_size; ++#endif /* !NOBIOS */ + PCIBus *pci_bus; + CPUState *env; + NICInfo *nd; +@@ -647,6 +654,7 @@ static void pc_init1(uint64_t ram_size, + cpu_register_physical_memory(0, ram_size, 0); + #endif + ++#ifndef NOBIOS + /* BIOS load */ + bios_offset = ram_size + vga_ram_size; + vga_bios_offset = bios_offset + 256 * 1024; +@@ -675,6 +683,7 @@ static void pc_init1(uint64_t ram_size, + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_FILENAME); + } + ret = load_image(buf, phys_ram_base + vga_bios_offset); ++#endif /* !NOBIOS */ + + /* setup basic memory access */ + #ifndef CONFIG_DM /* HVM domain owns memory */ +@@ -682,6 +691,7 @@ static void pc_init1(uint64_t ram_size, + vga_bios_offset | IO_MEM_ROM); + #endif + ++#ifndef NOBIOS + /* map the last 128KB of the BIOS in ISA space */ + isa_bios_size = bios_size; + if (isa_bios_size > (128 * 1024)) +@@ -694,6 +704,7 @@ static void pc_init1(uint64_t ram_size, + /* map all the bios at the top of memory */ + cpu_register_physical_memory((uint32_t)(-bios_size), + bios_size, bios_offset | IO_MEM_ROM); ++#endif + + bochs_bios_init(); + diff --git a/tools/ioemu/patches/qemu-smp b/tools/ioemu/patches/qemu-smp new file mode 100644 index 0000000000..86d88571af --- /dev/null +++ b/tools/ioemu/patches/qemu-smp @@ -0,0 +1,48 @@ +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-07-12 11:35:01.687705323 +0100 ++++ ioemu/vl.c 2006-07-12 11:35:01.753697055 +0100 +@@ -159,6 +159,8 @@ + #define MAX_CPUS 1 + #endif + ++extern int vcpus; ++ + int xc_handle; + + char domain_name[1024] = { 'H','V', 'M', 'X', 'E', 'N', '-'}; +@@ -4635,6 +4637,7 @@ + "-m megs set virtual RAM size to megs MB [default=%d]\n" + "-smp n set the number of CPUs to 'n' [default=1]\n" + "-nographic disable graphical output and redirect serial I/Os to console\n" ++ "-vcpus set CPU number of guest platform\n" + #ifndef _WIN32 + "-k language use keyboard layout (for example \"fr\" for French)\n" + #endif +@@ -4809,6 +4812,7 @@ + QEMU_OPTION_vnc, + + QEMU_OPTION_d, ++ QEMU_OPTION_vcpus, + }; + + typedef struct QEMUOption { +@@ -4886,6 +4890,7 @@ + { "cirrusvga", 0, QEMU_OPTION_cirrusvga }, + + { "d", HAS_ARG, QEMU_OPTION_d }, ++ { "vcpus", 1, QEMU_OPTION_vcpus }, + { NULL }, + }; + +@@ -5508,6 +5513,10 @@ + domid = atoi(optarg); + fprintf(logfile, "domid: %d\n", domid); + break; ++ case QEMU_OPTION_vcpus: ++ vcpus = atoi(optarg); ++ fprintf(logfile, "qemu: the number of cpus is %d\n", vcpus); ++ break; + } + } + } diff --git a/tools/ioemu/patches/qemu-target-i386-dm b/tools/ioemu/patches/qemu-target-i386-dm new file mode 100644 index 0000000000..c99a082d9d --- /dev/null +++ b/tools/ioemu/patches/qemu-target-i386-dm @@ -0,0 +1,1334 @@ +diff -r 9af27fed6713 Makefile.target +--- a/Makefile.target Wed Jun 21 17:46:33 2006 +0100 ++++ b/Makefile.target Mon Jun 26 15:59:21 2006 +0100 +@@ -57,6 +57,8 @@ QEMU_SYSTEM=qemu-fast + QEMU_SYSTEM=qemu-fast + endif + ++QEMU_SYSTEM=qemu-dm ++ + ifdef CONFIG_USER_ONLY + PROGS=$(QEMU_USER) + else +@@ -274,6 +276,9 @@ OBJS+=gdbstub.o + OBJS+=gdbstub.o + endif + ++# qemu-dm objects ++LIBOBJS=helper2.o exec-dm.o i8259-dm.o ++ + all: $(PROGS) + + $(QEMU_USER): $(OBJS) +@@ -328,7 +333,7 @@ ifeq ($(TARGET_BASE_ARCH), i386) + ifeq ($(TARGET_BASE_ARCH), i386) + # Hardware support + VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) +-VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o ++VL_OBJS+= fdc.o mc146818rtc.o serial.o i8254.o pcspk.o pc.o + VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o + DEFINES += -DHAS_AUDIO + endif +diff -r 9af27fed6713 configure +--- a/configure Wed Jun 21 17:46:33 2006 +0100 ++++ b/configure Mon Jun 26 15:59:21 2006 +0100 +@@ -359,6 +359,8 @@ if test -z "$target_list" ; then + if [ "$user" = "yes" ] ; then + target_list="i386-user arm-user armeb-user sparc-user ppc-user mips-user mipsel-user $target_list" + fi ++# the i386-dm target ++ target_list="i386-dm" + else + target_list=`echo "$target_list" | sed -e 's/,/ /g'` + fi +diff -r 9af27fed6713 monitor.c +--- a/monitor.c Wed Jun 21 17:46:33 2006 +0100 ++++ b/monitor.c Mon Jun 26 15:59:21 2006 +0100 +@@ -1142,6 +1142,10 @@ static term_cmd_t info_cmds[] = { + "", "show host USB devices", }, + { "profile", "", do_info_profile, + "", "show profiling information", }, ++#ifdef CONFIG_DM ++ { "hvmiopage", "", sp_info, ++ "", "show HVM device model shared page info", }, ++#endif /* CONFIG_DM */ + { NULL, NULL, }, + }; + +diff -r 9af27fed6713 vl.c +--- a/vl.c Wed Jun 21 17:46:33 2006 +0100 ++++ b/vl.c Mon Jun 26 15:59:21 2006 +0100 +@@ -87,7 +87,7 @@ + + #include "exec-all.h" + +-#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" ++#define DEFAULT_NETWORK_SCRIPT "/etc/xen/qemu-ifup" + + //#define DEBUG_UNUSED_IOPORT + //#define DEBUG_IOPORT +@@ -4382,7 +4382,7 @@ typedef struct QEMUResetEntry { + + static QEMUResetEntry *first_reset_entry; + static int reset_requested; +-static int shutdown_requested; ++int shutdown_requested; + static int powerdown_requested; + + void qemu_register_reset(QEMUResetHandler *func, void *opaque) +@@ -4534,6 +4534,7 @@ void main_loop_wait(int timeout) + qemu_get_clock(rt_clock)); + } + ++#ifndef CONFIG_DM + static CPUState *cur_cpu; + + int main_loop(void) +@@ -4608,6 +4609,7 @@ int main_loop(void) + cpu_disable_ticks(); + return ret; + } ++#endif /* !CONFIG_DM */ + + void help(void) + { +diff -r 9af27fed6713 vl.h +--- a/vl.h Wed Jun 21 17:46:33 2006 +0100 ++++ b/vl.h Mon Jun 26 15:59:21 2006 +0100 +@@ -38,6 +38,8 @@ + #include + #include + #include "audio/audio.h" ++#include "xenctrl.h" ++#include "xs.h" + + #ifndef O_LARGEFILE + #define O_LARGEFILE 0 +@@ -130,6 +132,11 @@ void qemu_system_powerdown(void); + #endif + + void main_loop_wait(int timeout); ++ ++extern FILE *logfile; ++ ++extern int xc_handle; ++extern int domid; + + extern int ram_size; + extern int bios_size; +@@ -814,6 +821,7 @@ uint32_t pic_intack_read(PicState2 *s); + uint32_t pic_intack_read(PicState2 *s); + void pic_info(void); + void irq_info(void); ++void sp_info(void); + + /* APIC */ + typedef struct IOAPICState IOAPICState; +diff -r 9af27fed6713 target-i386-dm/cpu.h +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/target-i386-dm/cpu.h Mon Jun 26 15:59:21 2006 +0100 +@@ -0,0 +1,86 @@ ++/* ++ * i386 virtual CPU header ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef CPU_I386_H ++#define CPU_I386_H ++ ++#include "config.h" ++ ++#ifdef TARGET_X86_64 ++#define TARGET_LONG_BITS 64 ++#else ++#define TARGET_LONG_BITS 32 ++#endif ++ ++/* target supports implicit self modifying code */ ++#define TARGET_HAS_SMC ++/* support for self modifying code even if the modified instruction is ++ close to the modifying instruction */ ++#define TARGET_HAS_PRECISE_SMC ++ ++#include "cpu-defs.h" ++ ++#include "softfloat.h" ++ ++#if defined(__i386__) && !defined(CONFIG_SOFTMMU) ++#define USE_CODE_COPY ++#endif ++ ++#ifdef USE_X86LDOUBLE ++typedef floatx80 CPU86_LDouble; ++#else ++typedef float64 CPU86_LDouble; ++#endif ++ ++/* Empty for now */ ++typedef struct CPUX86State { ++ uint32_t a20_mask; ++ ++ int interrupt_request; ++ ++ CPU_COMMON ++ ++ int send_event; ++} CPUX86State; ++ ++CPUX86State *cpu_x86_init(void); ++int cpu_x86_exec(CPUX86State *s); ++void cpu_x86_close(CPUX86State *s); ++int cpu_get_pic_interrupt(CPUX86State *s); ++/* MSDOS compatibility mode FPU exception support */ ++void cpu_set_ferr(CPUX86State *s); ++ ++void cpu_x86_set_a20(CPUX86State *env, int a20_state); ++ ++#ifndef IN_OP_I386 ++void cpu_x86_outb(CPUX86State *env, int addr, int val); ++void cpu_x86_outw(CPUX86State *env, int addr, int val); ++void cpu_x86_outl(CPUX86State *env, int addr, int val); ++int cpu_x86_inb(CPUX86State *env, int addr); ++int cpu_x86_inw(CPUX86State *env, int addr); ++int cpu_x86_inl(CPUX86State *env, int addr); ++#endif ++ ++/* helper2.c */ ++int main_loop(void); ++ ++#define TARGET_PAGE_BITS 12 ++#include "cpu-all.h" ++ ++#endif /* CPU_I386_H */ +diff -r 9af27fed6713 target-i386-dm/exec-dm.c +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/target-i386-dm/exec-dm.c Mon Jun 26 15:59:21 2006 +0100 +@@ -0,0 +1,512 @@ ++/* ++ * virtual page mapping and translated block handling ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#include "config.h" ++#ifdef _WIN32 ++#include ++#else ++#include ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cpu.h" ++#include "exec-all.h" ++ ++//#define DEBUG_TB_INVALIDATE ++//#define DEBUG_FLUSH ++//#define DEBUG_TLB ++ ++/* make various TB consistency checks */ ++//#define DEBUG_TB_CHECK ++//#define DEBUG_TLB_CHECK ++ ++#ifndef CONFIG_DM ++/* threshold to flush the translated code buffer */ ++#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE) ++ ++#define SMC_BITMAP_USE_THRESHOLD 10 ++ ++#define MMAP_AREA_START 0x00000000 ++#define MMAP_AREA_END 0xa8000000 ++ ++TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; ++TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; ++TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; ++int nb_tbs; ++/* any access to the tbs or the page table must use this lock */ ++spinlock_t tb_lock = SPIN_LOCK_UNLOCKED; ++ ++uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; ++uint8_t *code_gen_ptr; ++#endif /* !CONFIG_DM */ ++ ++uint64_t phys_ram_size; ++int phys_ram_fd; ++uint8_t *phys_ram_base; ++uint8_t *phys_ram_dirty; ++ ++CPUState *first_cpu; ++/* current CPU in the current thread. It is only valid inside ++ cpu_exec() */ ++CPUState *cpu_single_env; ++ ++typedef struct PageDesc { ++ /* list of TBs intersecting this ram page */ ++ TranslationBlock *first_tb; ++ /* in order to optimize self modifying code, we count the number ++ of lookups we do to a given page to use a bitmap */ ++ unsigned int code_write_count; ++ uint8_t *code_bitmap; ++#if defined(CONFIG_USER_ONLY) ++ unsigned long flags; ++#endif ++} PageDesc; ++ ++typedef struct PhysPageDesc { ++ /* offset in host memory of the page + io_index in the low 12 bits */ ++ unsigned long phys_offset; ++} PhysPageDesc; ++ ++typedef struct VirtPageDesc { ++ /* physical address of code page. It is valid only if 'valid_tag' ++ matches 'virt_valid_tag' */ ++ target_ulong phys_addr; ++ unsigned int valid_tag; ++#if !defined(CONFIG_SOFTMMU) ++ /* original page access rights. It is valid only if 'valid_tag' ++ matches 'virt_valid_tag' */ ++ unsigned int prot; ++#endif ++} VirtPageDesc; ++ ++#define L2_BITS 10 ++#define L1_BITS (32 - L2_BITS - TARGET_PAGE_BITS) ++ ++#define L1_SIZE (1 << L1_BITS) ++#define L2_SIZE (1 << L2_BITS) ++ ++unsigned long qemu_real_host_page_size; ++unsigned long qemu_host_page_bits; ++unsigned long qemu_host_page_size; ++unsigned long qemu_host_page_mask; ++ ++/* io memory support */ ++CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; ++CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4]; ++void *io_mem_opaque[IO_MEM_NB_ENTRIES]; ++static int io_mem_nb = 1; ++ ++/* log support */ ++char *logfilename = "/tmp/qemu.log"; ++FILE *logfile; ++int loglevel; ++ ++void cpu_exec_init(CPUState *env) ++{ ++ CPUState **penv; ++ int cpu_index; ++ ++ env->next_cpu = NULL; ++ penv = &first_cpu; ++ cpu_index = 0; ++ while (*penv != NULL) { ++ penv = (CPUState **)&(*penv)->next_cpu; ++ cpu_index++; ++ } ++ env->cpu_index = cpu_index; ++ *penv = env; ++ ++ /* alloc dirty bits array */ ++ phys_ram_dirty = qemu_malloc(phys_ram_size >> TARGET_PAGE_BITS); ++} ++ ++/* enable or disable low levels log */ ++void cpu_set_log(int log_flags) ++{ ++ loglevel = log_flags; ++ if (!logfile) { ++ logfile = fopen(logfilename, "w"); ++ if (!logfile) { ++ perror(logfilename); ++ _exit(1); ++ } ++#if !defined(CONFIG_SOFTMMU) ++ /* must avoid mmap() usage of glibc by setting a buffer "by hand" */ ++ { ++ static uint8_t logfile_buf[4096]; ++ setvbuf(logfile, logfile_buf, _IOLBF, sizeof(logfile_buf)); ++ } ++#else ++ setvbuf(logfile, NULL, _IOLBF, 0); ++#endif ++ stdout = logfile; ++ stderr = logfile; ++ } ++} ++ ++void cpu_set_log_filename(const char *filename) ++{ ++ logfilename = strdup(filename); ++} ++ ++/* mask must never be zero, except for A20 change call */ ++void cpu_interrupt(CPUState *env, int mask) ++{ ++ env->interrupt_request |= mask; ++} ++ ++void cpu_reset_interrupt(CPUState *env, int mask) ++{ ++ env->interrupt_request &= ~mask; ++} ++ ++CPULogItem cpu_log_items[] = { ++ { CPU_LOG_TB_OUT_ASM, "out_asm", ++ "show generated host assembly code for each compiled TB" }, ++ { CPU_LOG_TB_IN_ASM, "in_asm", ++ "show target assembly code for each compiled TB" }, ++ { CPU_LOG_TB_OP, "op", ++ "show micro ops for each compiled TB (only usable if 'in_asm' used)" }, ++#ifdef TARGET_I386 ++ { CPU_LOG_TB_OP_OPT, "op_opt", ++ "show micro ops after optimization for each compiled TB" }, ++#endif ++ { CPU_LOG_INT, "int", ++ "show interrupts/exceptions in short format" }, ++ { CPU_LOG_EXEC, "exec", ++ "show trace before each executed TB (lots of logs)" }, ++ { CPU_LOG_TB_CPU, "cpu", ++ "show CPU state before bloc translation" }, ++#ifdef TARGET_I386 ++ { CPU_LOG_PCALL, "pcall", ++ "show protected mode far calls/returns/exceptions" }, ++#endif ++#ifdef DEBUG_IOPORT ++ { CPU_LOG_IOPORT, "ioport", ++ "show all i/o ports accesses" }, ++#endif ++ { 0, NULL, NULL }, ++}; ++ ++static int cmp1(const char *s1, int n, const char *s2) ++{ ++ if (strlen(s2) != n) ++ return 0; ++ return memcmp(s1, s2, n) == 0; ++} ++ ++/* takes a comma separated list of log masks. Return 0 if error. */ ++int cpu_str_to_log_mask(const char *str) ++{ ++ CPULogItem *item; ++ int mask; ++ const char *p, *p1; ++ ++ p = str; ++ mask = 0; ++ for(;;) { ++ p1 = strchr(p, ','); ++ if (!p1) ++ p1 = p + strlen(p); ++ if(cmp1(p,p1-p,"all")) { ++ for(item = cpu_log_items; item->mask != 0; item++) { ++ mask |= item->mask; ++ } ++ } else { ++ for(item = cpu_log_items; item->mask != 0; item++) { ++ if (cmp1(p, p1 - p, item->name)) ++ goto found; ++ } ++ return 0; ++ } ++ found: ++ mask |= item->mask; ++ if (*p1 != ',') ++ break; ++ p = p1 + 1; ++ } ++ return mask; ++} ++ ++void cpu_abort(CPUState *env, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ fprintf(stderr, "qemu: fatal: "); ++ vfprintf(stderr, fmt, ap); ++ fprintf(stderr, "\n"); ++ va_end(ap); ++ abort(); ++} ++ ++ ++/* XXX: Simple implementation. Fix later */ ++#define MAX_MMIO 32 ++struct mmio_space { ++ target_phys_addr_t start; ++ unsigned long size; ++ unsigned long io_index; ++} mmio[MAX_MMIO]; ++unsigned long mmio_cnt; ++ ++/* register physical memory. 'size' must be a multiple of the target ++ page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an ++ io memory page */ ++void cpu_register_physical_memory(target_phys_addr_t start_addr, ++ unsigned long size, ++ unsigned long phys_offset) ++{ ++ int i; ++ ++ for (i = 0; i < mmio_cnt; i++) { ++ if(mmio[i].start == start_addr) { ++ mmio[i].io_index = phys_offset; ++ mmio[i].size = size; ++ return; ++ } ++ } ++ ++ if (mmio_cnt == MAX_MMIO) { ++ fprintf(logfile, "too many mmio regions\n"); ++ exit(-1); ++ } ++ ++ mmio[mmio_cnt].io_index = phys_offset; ++ mmio[mmio_cnt].start = start_addr; ++ mmio[mmio_cnt++].size = size; ++} ++ ++/* mem_read and mem_write are arrays of functions containing the ++ function to access byte (index 0), word (index 1) and dword (index ++ 2). All functions must be supplied. If io_index is non zero, the ++ corresponding io zone is modified. If it is zero, a new io zone is ++ allocated. The return value can be used with ++ cpu_register_physical_memory(). (-1) is returned if error. */ ++int cpu_register_io_memory(int io_index, ++ CPUReadMemoryFunc **mem_read, ++ CPUWriteMemoryFunc **mem_write, ++ void *opaque) ++{ ++ int i; ++ ++ if (io_index <= 0) { ++ if (io_index >= IO_MEM_NB_ENTRIES) ++ return -1; ++ io_index = io_mem_nb++; ++ } else { ++ if (io_index >= IO_MEM_NB_ENTRIES) ++ return -1; ++ } ++ ++ for(i = 0;i < 3; i++) { ++ io_mem_read[io_index][i] = mem_read[i]; ++ io_mem_write[io_index][i] = mem_write[i]; ++ } ++ io_mem_opaque[io_index] = opaque; ++ return io_index << IO_MEM_SHIFT; ++} ++ ++CPUWriteMemoryFunc **cpu_get_io_memory_write(int io_index) ++{ ++ return io_mem_write[io_index >> IO_MEM_SHIFT]; ++} ++ ++CPUReadMemoryFunc **cpu_get_io_memory_read(int io_index) ++{ ++ return io_mem_read[io_index >> IO_MEM_SHIFT]; ++} ++ ++/* physical memory access (slow version, mainly for debug) */ ++#if defined(CONFIG_USER_ONLY) ++void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, ++ int len, int is_write) ++{ ++ int l, flags; ++ target_ulong page; ++ ++ while (len > 0) { ++ page = addr & TARGET_PAGE_MASK; ++ l = (page + TARGET_PAGE_SIZE) - addr; ++ if (l > len) ++ l = len; ++ flags = page_get_flags(page); ++ if (!(flags & PAGE_VALID)) ++ return; ++ if (is_write) { ++ if (!(flags & PAGE_WRITE)) ++ return; ++ memcpy((uint8_t *)addr, buf, len); ++ } else { ++ if (!(flags & PAGE_READ)) ++ return; ++ memcpy(buf, (uint8_t *)addr, len); ++ } ++ len -= l; ++ buf += l; ++ addr += l; ++ } ++} ++#else ++ ++int iomem_index(target_phys_addr_t addr) ++{ ++ int i; ++ ++ for (i = 0; i < mmio_cnt; i++) { ++ unsigned long start, end; ++ ++ start = mmio[i].start; ++ end = mmio[i].start + mmio[i].size; ++ ++ if ((addr >= start) && (addr <= end)){ ++ return (mmio[i].io_index >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); ++ } ++ } ++ return 0; ++} ++ ++void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, ++ int len, int is_write) ++{ ++ int l, io_index; ++ uint8_t *ptr; ++ uint32_t val; ++ target_phys_addr_t page; ++ unsigned long pd; ++ ++ while (len > 0) { ++ page = addr & TARGET_PAGE_MASK; ++ l = (page + TARGET_PAGE_SIZE) - addr; ++ if (l > len) ++ l = len; ++ ++ pd = page; ++ io_index = iomem_index(page); ++ if (is_write) { ++ if (io_index) { ++ if (l >= 4 && ((addr & 3) == 0)) { ++ /* 32 bit read access */ ++ val = ldl_raw(buf); ++ io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val); ++ l = 4; ++ } else if (l >= 2 && ((addr & 1) == 0)) { ++ /* 16 bit read access */ ++ val = lduw_raw(buf); ++ io_mem_write[io_index][1](io_mem_opaque[io_index], addr, val); ++ l = 2; ++ } else { ++ /* 8 bit access */ ++ val = ldub_raw(buf); ++ io_mem_write[io_index][0](io_mem_opaque[io_index], addr, val); ++ l = 1; ++ } ++ } else { ++ unsigned long addr1; ++ ++ addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); ++ /* RAM case */ ++ ptr = phys_ram_base + addr1; ++ memcpy(ptr, buf, l); ++ } ++ } else { ++ if (io_index) { ++ if (l >= 4 && ((addr & 3) == 0)) { ++ /* 32 bit read access */ ++ val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr); ++ stl_raw(buf, val); ++ l = 4; ++ } else if (l >= 2 && ((addr & 1) == 0)) { ++ /* 16 bit read access */ ++ val = io_mem_read[io_index][1](io_mem_opaque[io_index], addr); ++ stw_raw(buf, val); ++ l = 2; ++ } else { ++ /* 8 bit access */ ++ val = io_mem_read[io_index][0](io_mem_opaque[io_index], addr); ++ stb_raw(buf, val); ++ l = 1; ++ } ++ } else { ++ /* RAM case */ ++ ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + ++ (addr & ~TARGET_PAGE_MASK); ++ memcpy(buf, ptr, l); ++ } ++ } ++ len -= l; ++ buf += l; ++ addr += l; ++ } ++} ++#endif ++ ++/* virtual memory access for debug */ ++int cpu_memory_rw_debug(CPUState *env, target_ulong addr, ++ uint8_t *buf, int len, int is_write) ++{ ++ int l; ++ target_ulong page, phys_addr; ++ ++ while (len > 0) { ++ page = addr & TARGET_PAGE_MASK; ++ phys_addr = cpu_get_phys_page_debug(env, page); ++ /* if no physical page mapped, return an error */ ++ if (phys_addr == -1) ++ return -1; ++ l = (page + TARGET_PAGE_SIZE) - addr; ++ if (l > len) ++ l = len; ++ cpu_physical_memory_rw(phys_addr + (addr & ~TARGET_PAGE_MASK), ++ buf, l, is_write); ++ len -= l; ++ buf += l; ++ addr += l; ++ } ++ return 0; ++} ++ ++void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, ++ int dirty_flags) ++{ ++ unsigned long length; ++ int i, mask, len; ++ uint8_t *p; ++ ++ start &= TARGET_PAGE_MASK; ++ end = TARGET_PAGE_ALIGN(end); ++ ++ length = end - start; ++ if (length == 0) ++ return; ++ mask = ~dirty_flags; ++ p = phys_ram_dirty + (start >> TARGET_PAGE_BITS); ++ len = length >> TARGET_PAGE_BITS; ++ for(i = 0; i < len; i++) ++ p[i] &= mask; ++ ++ return; ++} +diff -r 9af27fed6713 target-i386-dm/helper2.c +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/target-i386-dm/helper2.c Mon Jun 26 15:59:21 2006 +0100 +@@ -0,0 +1,464 @@ ++/* ++ * i386 helpers (without register variable usage) ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* ++ * Main cpu loop for handling I/O requests coming from a virtual machine ++ * Copyright © 2004, Intel Corporation. ++ * Copyright © 2005, International Business Machines Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU Lesser General Public License, ++ * version 2.1, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "cpu.h" ++#include "exec-all.h" ++ ++//#define DEBUG_MMU ++ ++#ifdef USE_CODE_COPY ++#include ++#include ++#include ++ ++_syscall3(int, modify_ldt, int, func, void *, ptr, unsigned long, bytecount) ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 66) ++#define modify_ldt_ldt_s user_desc ++#endif ++#endif /* USE_CODE_COPY */ ++ ++#include "vl.h" ++ ++int domid = -1; ++int vcpus = 1; ++ ++int xc_handle; ++ ++shared_iopage_t *shared_page = NULL; ++ ++/* the evtchn fd for polling */ ++int xce_handle = -1; ++ ++/* which vcpu we are serving */ ++int send_vcpu = 0; ++ ++CPUX86State *cpu_x86_init(void) ++{ ++ CPUX86State *env; ++ static int inited; ++ int i, rc; ++ ++ env = qemu_mallocz(sizeof(CPUX86State)); ++ if (!env) ++ return NULL; ++ cpu_exec_init(env); ++ ++ /* init various static tables */ ++ if (!inited) { ++ inited = 1; ++ ++ cpu_single_env = env; ++ ++ xce_handle = xc_evtchn_open(); ++ if (xce_handle == -1) { ++ perror("open"); ++ return NULL; ++ } ++ ++ /* FIXME: how about if we overflow the page here? */ ++ for (i = 0; i < vcpus; i++) { ++ rc = xc_evtchn_bind_interdomain( ++ xce_handle, domid, shared_page->vcpu_iodata[i].vp_eport); ++ if (rc == -1) { ++ fprintf(logfile, "bind interdomain ioctl error %d\n", errno); ++ return NULL; ++ } ++ shared_page->vcpu_iodata[i].dm_eport = rc; ++ } ++ } ++ ++ return env; ++} ++ ++/* called from main_cpu_reset */ ++void cpu_reset(CPUX86State *env) ++{ ++} ++ ++void cpu_x86_close(CPUX86State *env) ++{ ++ free(env); ++} ++ ++ ++void cpu_dump_state(CPUState *env, FILE *f, ++ int (*cpu_fprintf)(FILE *f, const char *fmt, ...), ++ int flags) ++{ ++} ++ ++/***********************************************************/ ++/* x86 mmu */ ++/* XXX: add PGE support */ ++ ++void cpu_x86_set_a20(CPUX86State *env, int a20_state) ++{ ++ a20_state = (a20_state != 0); ++ if (a20_state != ((env->a20_mask >> 20) & 1)) { ++#if defined(DEBUG_MMU) ++ printf("A20 update: a20=%d\n", a20_state); ++#endif ++ env->a20_mask = 0xffefffff | (a20_state << 20); ++ } ++} ++ ++target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) ++{ ++ return addr; ++} ++ ++//some functions to handle the io req packet ++void sp_info() ++{ ++ ioreq_t *req; ++ int i; ++ ++ for (i = 0; i < vcpus; i++) { ++ req = &(shared_page->vcpu_iodata[i].vp_ioreq); ++ term_printf("vcpu %d: event port %d\n", i, ++ shared_page->vcpu_iodata[i].vp_eport); ++ term_printf(" req state: %x, pvalid: %x, addr: %"PRIx64", " ++ "data: %"PRIx64", count: %"PRIx64", size: %"PRIx64"\n", ++ req->state, req->pdata_valid, req->addr, ++ req->u.data, req->count, req->size); ++ term_printf(" IO totally occurred on this vcpu: %"PRIx64"\n", ++ req->io_count); ++ } ++} ++ ++//get the ioreq packets from share mem ++static ioreq_t *__cpu_get_ioreq(int vcpu) ++{ ++ ioreq_t *req; ++ ++ req = &(shared_page->vcpu_iodata[vcpu].vp_ioreq); ++ ++ if (req->state == STATE_IOREQ_READY) { ++ req->state = STATE_IOREQ_INPROCESS; ++ return req; ++ } ++ ++ fprintf(logfile, "False I/O request ... in-service already: " ++ "%x, pvalid: %x, port: %"PRIx64", " ++ "data: %"PRIx64", count: %"PRIx64", size: %"PRIx64"\n", ++ req->state, req->pdata_valid, req->addr, ++ req->u.data, req->count, req->size); ++ return NULL; ++} ++ ++//use poll to get the port notification ++//ioreq_vec--out,the ++//retval--the number of ioreq packet ++static ioreq_t *cpu_get_ioreq(void) ++{ ++ int i; ++ evtchn_port_t port; ++ ++ port = xc_evtchn_pending(xce_handle); ++ if (port != -1) { ++ for ( i = 0; i < vcpus; i++ ) ++ if ( shared_page->vcpu_iodata[i].dm_eport == port ) ++ break; ++ ++ if ( i == vcpus ) { ++ fprintf(logfile, "Fatal error while trying to get io event!\n"); ++ exit(1); ++ } ++ ++ // unmask the wanted port again ++ xc_evtchn_unmask(xce_handle, port); ++ ++ //get the io packet from shared memory ++ send_vcpu = i; ++ return __cpu_get_ioreq(i); ++ } ++ ++ //read error or read nothing ++ return NULL; ++} ++ ++unsigned long do_inp(CPUState *env, unsigned long addr, unsigned long size) ++{ ++ switch(size) { ++ case 1: ++ return cpu_inb(env, addr); ++ case 2: ++ return cpu_inw(env, addr); ++ case 4: ++ return cpu_inl(env, addr); ++ default: ++ fprintf(logfile, "inp: bad size: %lx %lx\n", addr, size); ++ exit(-1); ++ } ++} ++ ++void do_outp(CPUState *env, unsigned long addr, ++ unsigned long size, unsigned long val) ++{ ++ switch(size) { ++ case 1: ++ return cpu_outb(env, addr, val); ++ case 2: ++ return cpu_outw(env, addr, val); ++ case 4: ++ return cpu_outl(env, addr, val); ++ default: ++ fprintf(logfile, "outp: bad size: %lx %lx\n", addr, size); ++ exit(-1); ++ } ++} ++ ++extern void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, ++ int len, int is_write); ++ ++static inline void read_physical(uint64_t addr, unsigned long size, void *val) ++{ ++ return cpu_physical_memory_rw((target_phys_addr_t)addr, val, size, 0); ++} ++ ++static inline void write_physical(uint64_t addr, unsigned long size, void *val) ++{ ++ return cpu_physical_memory_rw((target_phys_addr_t)addr, val, size, 1); ++} ++ ++void cpu_ioreq_pio(CPUState *env, ioreq_t *req) ++{ ++ int i, sign; ++ ++ sign = req->df ? -1 : 1; ++ ++ if (req->dir == IOREQ_READ) { ++ if (!req->pdata_valid) { ++ req->u.data = do_inp(env, req->addr, req->size); ++ } else { ++ unsigned long tmp; ++ ++ for (i = 0; i < req->count; i++) { ++ tmp = do_inp(env, req->addr, req->size); ++ write_physical((target_phys_addr_t) req->u.pdata ++ + (sign * i * req->size), ++ req->size, &tmp); ++ } ++ } ++ } else if (req->dir == IOREQ_WRITE) { ++ if (!req->pdata_valid) { ++ do_outp(env, req->addr, req->size, req->u.data); ++ } else { ++ for (i = 0; i < req->count; i++) { ++ unsigned long tmp; ++ ++ read_physical((target_phys_addr_t) req->u.pdata ++ + (sign * i * req->size), ++ req->size, &tmp); ++ do_outp(env, req->addr, req->size, tmp); ++ } ++ } ++ } ++} ++ ++void cpu_ioreq_move(CPUState *env, ioreq_t *req) ++{ ++ int i, sign; ++ ++ sign = req->df ? -1 : 1; ++ ++ if (!req->pdata_valid) { ++ if (req->dir == IOREQ_READ) { ++ for (i = 0; i < req->count; i++) { ++ read_physical(req->addr ++ + (sign * i * req->size), ++ req->size, &req->u.data); ++ } ++ } else if (req->dir == IOREQ_WRITE) { ++ for (i = 0; i < req->count; i++) { ++ write_physical(req->addr ++ + (sign * i * req->size), ++ req->size, &req->u.data); ++ } ++ } ++ } else { ++ unsigned long tmp; ++ ++ if (req->dir == IOREQ_READ) { ++ for (i = 0; i < req->count; i++) { ++ read_physical(req->addr ++ + (sign * i * req->size), ++ req->size, &tmp); ++ write_physical((target_phys_addr_t )req->u.pdata ++ + (sign * i * req->size), ++ req->size, &tmp); ++ } ++ } else if (req->dir == IOREQ_WRITE) { ++ for (i = 0; i < req->count; i++) { ++ read_physical((target_phys_addr_t) req->u.pdata ++ + (sign * i * req->size), ++ req->size, &tmp); ++ write_physical(req->addr ++ + (sign * i * req->size), ++ req->size, &tmp); ++ } ++ } ++ } ++} ++ ++void cpu_ioreq_and(CPUState *env, ioreq_t *req) ++{ ++ unsigned long tmp1, tmp2; ++ ++ if (req->pdata_valid != 0) ++ hw_error("expected scalar value"); ++ ++ read_physical(req->addr, req->size, &tmp1); ++ if (req->dir == IOREQ_WRITE) { ++ tmp2 = tmp1 & (unsigned long) req->u.data; ++ write_physical(req->addr, req->size, &tmp2); ++ } ++ req->u.data = tmp1; ++} ++ ++void cpu_ioreq_or(CPUState *env, ioreq_t *req) ++{ ++ unsigned long tmp1, tmp2; ++ ++ if (req->pdata_valid != 0) ++ hw_error("expected scalar value"); ++ ++ read_physical(req->addr, req->size, &tmp1); ++ if (req->dir == IOREQ_WRITE) { ++ tmp2 = tmp1 | (unsigned long) req->u.data; ++ write_physical(req->addr, req->size, &tmp2); ++ } ++ req->u.data = tmp1; ++} ++ ++void cpu_ioreq_xor(CPUState *env, ioreq_t *req) ++{ ++ unsigned long tmp1, tmp2; ++ ++ if (req->pdata_valid != 0) ++ hw_error("expected scalar value"); ++ ++ read_physical(req->addr, req->size, &tmp1); ++ if (req->dir == IOREQ_WRITE) { ++ tmp2 = tmp1 ^ (unsigned long) req->u.data; ++ write_physical(req->addr, req->size, &tmp2); ++ } ++ req->u.data = tmp1; ++} ++ ++void cpu_handle_ioreq(void *opaque) ++{ ++ CPUState *env = opaque; ++ ioreq_t *req = cpu_get_ioreq(); ++ ++ if (req) { ++ if ((!req->pdata_valid) && (req->dir == IOREQ_WRITE)) { ++ if (req->size != 4) ++ req->u.data &= (1UL << (8 * req->size))-1; ++ } ++ ++ switch (req->type) { ++ case IOREQ_TYPE_PIO: ++ cpu_ioreq_pio(env, req); ++ break; ++ case IOREQ_TYPE_COPY: ++ cpu_ioreq_move(env, req); ++ break; ++ case IOREQ_TYPE_AND: ++ cpu_ioreq_and(env, req); ++ break; ++ case IOREQ_TYPE_OR: ++ cpu_ioreq_or(env, req); ++ break; ++ case IOREQ_TYPE_XOR: ++ cpu_ioreq_xor(env, req); ++ break; ++ default: ++ hw_error("Invalid ioreq type 0x%x\n", req->type); ++ } ++ ++ /* No state change if state = STATE_IORESP_HOOK */ ++ if (req->state == STATE_IOREQ_INPROCESS) ++ req->state = STATE_IORESP_READY; ++ env->send_event = 1; ++ } ++} ++ ++int main_loop(void) ++{ ++ extern int vm_running; ++ extern int shutdown_requested; ++ CPUState *env = cpu_single_env; ++ int evtchn_fd = xc_evtchn_fd(xce_handle); ++ ++ qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, env); ++ ++ env->send_event = 0; ++ ++ while (1) { ++ if (vm_running) { ++ if (shutdown_requested) ++ break; ++ } ++ ++ /* Wait up to 10 msec. */ ++ main_loop_wait(10); ++ ++ if (env->send_event) { ++ env->send_event = 0; ++ xc_evtchn_notify(xce_handle, ++ shared_page->vcpu_iodata[send_vcpu].dm_eport); ++ } ++ } ++ return 0; ++} +diff -r 9af27fed6713 target-i386-dm/i8259-dm.c +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/target-i386-dm/i8259-dm.c Mon Jun 26 15:59:21 2006 +0100 +@@ -0,0 +1,107 @@ ++/* Xen 8259 stub for interrupt controller emulation ++ * ++ * Copyright (c) 2003-2004 Fabrice Bellard ++ * Copyright (c) 2005 Intel corperation ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#include "vl.h" ++ ++/* debug PIC */ ++//#define DEBUG_PIC ++ ++//#define DEBUG_IRQ_LATENCY ++//#define DEBUG_IRQ_COUNT ++ ++#include "xenctrl.h" ++#include ++#include ++#include "cpu.h" ++#include "cpu-all.h" ++ ++extern shared_iopage_t *shared_page; ++ ++struct PicState2 { ++}; ++ ++void pic_set_irq_new(void *opaque, int irq, int level) ++{ ++ /* PicState2 *s = opaque; */ ++ global_iodata_t *gio; ++ int mask; ++ ++ gio = &shared_page->sp_global; ++ mask = 1 << irq; ++ if ( gio->pic_elcr & mask ) { ++ /* level */ ++ if ( level ) { ++ atomic_clear_bit(irq, &gio->pic_clear_irr); ++ atomic_set_bit(irq, &gio->pic_irr); ++ cpu_single_env->send_event = 1; ++ } ++ else { ++ atomic_clear_bit(irq, &gio->pic_irr); ++ atomic_set_bit(irq, &gio->pic_clear_irr); ++ cpu_single_env->send_event = 1; ++ } ++ } ++ else { ++ /* edge */ ++ if ( level ) { ++ if ( (mask & gio->pic_last_irr) == 0 ) { ++ atomic_set_bit(irq, &gio->pic_irr); ++ atomic_set_bit(irq, &gio->pic_last_irr); ++ cpu_single_env->send_event = 1; ++ } ++ } ++ else { ++ atomic_clear_bit(irq, &gio->pic_last_irr); ++ } ++ } ++} ++ ++/* obsolete function */ ++void pic_set_irq(int irq, int level) ++{ ++ pic_set_irq_new(isa_pic, irq, level); ++} ++ ++void irq_info(void) ++{ ++ term_printf("irq statistic code not compiled.\n"); ++} ++ ++void pic_info(void) ++{ ++ term_printf("pic_info code not compiled.\n"); ++} ++ ++PicState2 *pic_init(IRQRequestFunc *irq_request, void *irq_request_opaque) ++{ ++ PicState2 *s; ++ s = qemu_mallocz(sizeof(PicState2)); ++ if (!s) ++ return NULL; ++ return s; ++} ++ ++void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func, ++ void *alt_irq_opaque) ++{ ++} +diff -r 9af27fed6713 target-i386-dm/qemu-dm.debug +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/target-i386-dm/qemu-dm.debug Mon Jun 26 15:59:21 2006 +0100 +@@ -0,0 +1,5 @@ ++#!/bin/sh ++ ++echo $* > /tmp/args ++echo $DISPLAY >> /tmp/args ++exec /usr/lib/xen/bin/qemu-dm $* +diff -r 9af27fed6713 target-i386-dm/qemu-ifup +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/target-i386-dm/qemu-ifup Mon Jun 26 15:59:21 2006 +0100 +@@ -0,0 +1,10 @@ ++#!/bin/sh ++ ++#. /etc/rc.d/init.d/functions ++#ulimit -c unlimited ++ ++echo -c 'config qemu network with xen bridge for ' ++echo $* ++ ++ifconfig $1 0.0.0.0 up ++brctl addif $2 $1 diff --git a/tools/ioemu/patches/qemu-timer b/tools/ioemu/patches/qemu-timer new file mode 100644 index 0000000000..94cdc9e784 --- /dev/null +++ b/tools/ioemu/patches/qemu-timer @@ -0,0 +1,54 @@ +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-07-12 11:35:02.126650330 +0100 ++++ ioemu/vl.c 2006-07-12 11:35:02.273631916 +0100 +@@ -861,6 +861,16 @@ + } + } + ++#ifdef CONFIG_DM ++static void timer_save(QEMUFile *f, void *opaque) ++{ ++} ++ ++static int timer_load(QEMUFile *f, void *opaque, int version_id) ++{ ++ return 0; ++} ++#else /* !CONFIG_DM */ + static void timer_save(QEMUFile *f, void *opaque) + { + if (cpu_ticks_enabled) { +@@ -977,6 +987,8 @@ + + #endif /* !defined(_WIN32) */ + ++#endif /* !CONFIG_DM */ ++ + static void init_timers(void) + { + rt_clock = qemu_new_clock(QEMU_TIMER_REALTIME); +@@ -1011,12 +1023,15 @@ + pit_min_timer_count = ((uint64_t)10000 * PIT_FREQ) / 1000000; + #else + { ++#ifndef CONFIG_DM + struct sigaction act; + struct itimerval itv; ++#endif + + /* get times() syscall frequency */ + timer_freq = sysconf(_SC_CLK_TCK); + ++#ifndef CONFIG_DM + /* timer signal */ + sigfillset(&act.sa_mask); + act.sa_flags = 0; +@@ -1062,6 +1077,7 @@ + pit_min_timer_count = ((uint64_t)itv.it_interval.tv_usec * + PIT_FREQ) / 1000000; + } ++#endif /* CONFIG_DM */ + } + #endif + } diff --git a/tools/ioemu/patches/sdl-mouse-invisible-wall b/tools/ioemu/patches/sdl-mouse-invisible-wall new file mode 100644 index 0000000000..c0d7eef028 --- /dev/null +++ b/tools/ioemu/patches/sdl-mouse-invisible-wall @@ -0,0 +1,25 @@ +Index: ioemu/sdl.c +=================================================================== +--- ioemu.orig/sdl.c 2006-07-12 11:35:01.450735012 +0100 ++++ ioemu/sdl.c 2006-07-12 11:35:03.377493622 +0100 +@@ -280,13 +280,18 @@ + + static void sdl_hide_cursor(void) + { +- SDL_SetCursor(sdl_cursor_hidden); ++ if (kbd_mouse_is_absolute()) { ++ SDL_ShowCursor(1); ++ SDL_SetCursor(sdl_cursor_hidden); ++ } else { ++ SDL_ShowCursor(0); ++ } + } + + static void sdl_show_cursor(void) + { + if (!kbd_mouse_is_absolute()) { +- SDL_SetCursor(sdl_cursor_normal); ++ SDL_ShowCursor(1); + } + } + diff --git a/tools/ioemu/patches/serial-non-block b/tools/ioemu/patches/serial-non-block new file mode 100644 index 0000000000..adbbe24319 --- /dev/null +++ b/tools/ioemu/patches/serial-non-block @@ -0,0 +1,48 @@ +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-07-12 11:35:02.880555879 +0100 ++++ ioemu/vl.c 2006-07-12 11:35:03.004540346 +0100 +@@ -1215,19 +1215,34 @@ + + static int unix_write(int fd, const uint8_t *buf, int len1) + { +- int ret, len; ++ int ret, sel_ret, len; ++ int max_fd; ++ fd_set writefds; ++ struct timeval timeout; ++ ++ max_fd = fd; + + len = len1; + while (len > 0) { +- ret = write(fd, buf, len); +- if (ret < 0) { +- if (errno != EINTR && errno != EAGAIN) +- return -1; +- } else if (ret == 0) { +- break; ++ FD_ZERO(&writefds); ++ FD_SET(fd, &writefds); ++ timeout.tv_sec = 0; ++ timeout.tv_usec = 0; ++ sel_ret = select(max_fd + 1, NULL, &writefds, 0, &timeout); ++ if (sel_ret <= 0) { ++ /* Timeout or select error */ ++ return -1; + } else { +- buf += ret; +- len -= ret; ++ ret = write(fd, buf, len); ++ if (ret < 0) { ++ if (errno != EINTR && errno != EAGAIN) ++ return -1; ++ } else if (ret == 0) { ++ break; ++ } else { ++ buf += ret; ++ len -= ret; ++ } + } + } + return len1 - len; diff --git a/tools/ioemu/patches/series b/tools/ioemu/patches/series new file mode 100644 index 0000000000..3c3c025cce --- /dev/null +++ b/tools/ioemu/patches/series @@ -0,0 +1,32 @@ +xen-build +qemu-dm +qemu-target-i386-dm +qemu-cleanup +qemu-64bit +qemu-bugfixes +qemu-logging +qemu-infrastructure +qemu-hvm-banner +xen-domain-name +xen-domid +xen-mm +qemu-smp +qemu-no-apic +qemu-nobios +xen-network +qemu-timer +domain-reset +domain-destroy +support-xm-console +hypervisor-pit +shared-vram +shadow-vram +serial-non-block +ide-hd-multithread +domain-timeoffset +sdl-mouse-invisible-wall +acpi-support +acpi-timer-support +acpi-poweroff-support +vnc-cleanup +vnc-fixes diff --git a/tools/ioemu/patches/shadow-vram b/tools/ioemu/patches/shadow-vram new file mode 100644 index 0000000000..68468cb021 --- /dev/null +++ b/tools/ioemu/patches/shadow-vram @@ -0,0 +1,147 @@ +diff -r 0ef2ae12258c hw/vga.c +--- a/hw/vga.c Mon Jun 26 16:07:22 2006 +0100 ++++ b/hw/vga.c Tue Jun 27 09:42:44 2006 +0100 +@@ -1287,6 +1287,105 @@ void vga_invalidate_scanlines(VGAState * + } + } + ++static inline int cmp_vram(VGAState *s, int offset, int n) ++{ ++ long *vp, *sp; ++ ++ if (s->vram_shadow == NULL) ++ return 1; ++ vp = (long *)(s->vram_ptr + offset); ++ sp = (long *)(s->vram_shadow + offset); ++ while ((n -= sizeof(*vp)) >= 0) { ++ if (*vp++ != *sp++) { ++ memcpy(sp - 1, vp - 1, n + sizeof(*vp)); ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++#ifdef USE_SSE2 ++ ++#include ++#include ++#include ++ ++int sse2_ok = 1; ++ ++static inline unsigned int cpuid_edx(unsigned int op) ++{ ++ unsigned int eax, edx; ++ ++#ifdef __x86_64__ ++#define __bx "rbx" ++#else ++#define __bx "ebx" ++#endif ++ __asm__("push %%"__bx"; cpuid; pop %%"__bx ++ : "=a" (eax), "=d" (edx) ++ : "0" (op) ++ : "cx"); ++#undef __bx ++ ++ return edx; ++} ++ ++jmp_buf sse_jbuf; ++ ++void intr(int sig) ++{ ++ sse2_ok = 0; ++ longjmp(sse_jbuf, 1); ++} ++ ++void check_sse2(void) ++{ ++ /* Check 1: What does CPUID say? */ ++ if ((cpuid_edx(1) & 0x4000000) == 0) { ++ sse2_ok = 0; ++ return; ++ } ++ ++ /* Check 2: Can we use SSE2 in anger? */ ++ signal(SIGILL, intr); ++ if (setjmp(sse_jbuf) == 0) ++ __asm__("xorps %xmm0,%xmm0\n"); ++} ++ ++int vram_dirty(VGAState *s, int offset, int n) ++{ ++ __m128i *sp, *vp; ++ ++ if (s->vram_shadow == NULL) ++ return 1; ++ if (sse2_ok == 0) ++ return cmp_vram(s, offset, n); ++ vp = (__m128i *)(s->vram_ptr + offset); ++ sp = (__m128i *)(s->vram_shadow + offset); ++ while ((n -= sizeof(*vp)) >= 0) { ++ if (_mm_movemask_epi8(_mm_cmpeq_epi8(*sp, *vp)) != 0xffff) { ++ while (n >= 0) { ++ _mm_store_si128(sp++, _mm_load_si128(vp++)); ++ n -= sizeof(*vp); ++ } ++ return 1; ++ } ++ sp++; ++ vp++; ++ } ++ return 0; ++} ++#else /* !USE_SSE2 */ ++int vram_dirty(VGAState *s, int offset, int n) ++{ ++ return cmp_vram(s, offset, n); ++} ++ ++void check_sse2(void) ++{ ++} ++#endif /* !USE_SSE2 */ ++ + /* + * graphic modes + */ +@@ -1381,6 +1480,11 @@ static void vga_draw_graphic(VGAState *s + printf("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n", + width, height, v, line_offset, s->cr[9], s->cr[0x17], s->line_compare, s->sr[0x01]); + #endif ++ ++ for (y = 0; y < s->vram_size; y += TARGET_PAGE_SIZE) ++ if (vram_dirty(s, y, TARGET_PAGE_SIZE)) ++ cpu_physical_memory_set_dirty(s->vram_offset + y); ++ + addr1 = (s->start_addr * 4); + bwidth = width * 4; + y_start = -1; +@@ -1699,6 +1803,14 @@ void vga_common_init(VGAState *s, Displa + + vga_reset(s); + ++ check_sse2(); ++ s->vram_shadow = qemu_malloc(vga_ram_size+TARGET_PAGE_SIZE+1); ++ if (s->vram_shadow == NULL) ++ fprintf(stderr, "Cannot allocate %d bytes for VRAM shadow, " ++ "mouse will be slow\n", vga_ram_size); ++ s->vram_shadow = (uint8_t *)((long)(s->vram_shadow + TARGET_PAGE_SIZE - 1) ++ & ~(TARGET_PAGE_SIZE - 1)); ++ + s->vram_ptr = qemu_malloc(vga_ram_size); + s->vram_offset = vga_ram_offset; + s->vram_size = vga_ram_size; +diff -r 0ef2ae12258c hw/vga_int.h +--- a/hw/vga_int.h Mon Jun 26 16:07:22 2006 +0100 ++++ b/hw/vga_int.h Tue Jun 27 09:42:44 2006 +0100 +@@ -76,6 +76,7 @@ + + #define VGA_STATE_COMMON \ + uint8_t *vram_ptr; \ ++ uint8_t *vram_shadow; \ + unsigned long vram_offset; \ + unsigned int vram_size; \ + unsigned long bios_offset; \ diff --git a/tools/ioemu/patches/shared-vram b/tools/ioemu/patches/shared-vram new file mode 100644 index 0000000000..814a5a45b1 --- /dev/null +++ b/tools/ioemu/patches/shared-vram @@ -0,0 +1,357 @@ +diff -r 62e05863ec04 hw/cirrus_vga.c +--- a/hw/cirrus_vga.c Mon Jun 26 15:18:40 2006 +0100 ++++ b/hw/cirrus_vga.c Mon Jun 26 15:19:40 2006 +0100 +@@ -28,6 +28,9 @@ + */ + #include "vl.h" + #include "vga_int.h" ++#ifndef _WIN32 ++#include ++#endif + + /* + * TODO: +@@ -231,6 +234,8 @@ typedef struct CirrusVGAState { + int cirrus_linear_io_addr; + int cirrus_linear_bitblt_io_addr; + int cirrus_mmio_io_addr; ++ unsigned long cirrus_lfb_addr; ++ unsigned long cirrus_lfb_end; + uint32_t cirrus_addr_mask; + uint32_t linear_mmio_mask; + uint8_t cirrus_shadow_gr0; +@@ -267,6 +272,8 @@ typedef struct CirrusVGAState { + int last_hw_cursor_y_end; + int real_vram_size; /* XXX: suppress that */ + CPUWriteMemoryFunc **cirrus_linear_write; ++ unsigned long map_addr; ++ unsigned long map_end; + } CirrusVGAState; + + typedef struct PCICirrusVGAState { +@@ -276,6 +283,8 @@ typedef struct PCICirrusVGAState { + + static uint8_t rop_to_index[256]; + ++void *shared_vram; ++ + /*************************************** + * + * prototypes. +@@ -2520,6 +2529,80 @@ static CPUWriteMemoryFunc *cirrus_linear + cirrus_linear_bitblt_writel, + }; + ++static void *set_vram_mapping(unsigned long begin, unsigned long end) ++{ ++ xen_pfn_t *extent_start = NULL; ++ unsigned long nr_extents; ++ void *vram_pointer = NULL; ++ int i; ++ ++ /* align begin and end address */ ++ begin = begin & TARGET_PAGE_MASK; ++ end = begin + VGA_RAM_SIZE; ++ end = (end + TARGET_PAGE_SIZE -1 ) & TARGET_PAGE_MASK; ++ nr_extents = (end - begin) >> TARGET_PAGE_BITS; ++ ++ extent_start = malloc(sizeof(xen_pfn_t) * nr_extents); ++ if (extent_start == NULL) { ++ fprintf(stderr, "Failed malloc on set_vram_mapping\n"); ++ return NULL; ++ } ++ ++ memset(extent_start, 0, sizeof(xen_pfn_t) * nr_extents); ++ ++ for (i = 0; i < nr_extents; i++) ++ extent_start[i] = (begin + i * TARGET_PAGE_SIZE) >> TARGET_PAGE_BITS; ++ ++ set_mm_mapping(xc_handle, domid, nr_extents, 0, extent_start); ++ ++ vram_pointer = xc_map_foreign_batch(xc_handle, domid, ++ PROT_READ|PROT_WRITE, ++ extent_start, nr_extents); ++ if (vram_pointer == NULL) { ++ fprintf(logfile, "xc_map_foreign_batch vgaram returned error %d\n", ++ errno); ++ return NULL; ++ } ++ ++ memset(vram_pointer, 0, nr_extents * TARGET_PAGE_SIZE); ++ ++ free(extent_start); ++ ++ return vram_pointer; ++} ++ ++static int unset_vram_mapping(unsigned long begin, unsigned long end) ++{ ++ xen_pfn_t *extent_start = NULL; ++ unsigned long nr_extents; ++ int i; ++ ++ /* align begin and end address */ ++ ++ end = begin + VGA_RAM_SIZE; ++ begin = begin & TARGET_PAGE_MASK; ++ end = (end + TARGET_PAGE_SIZE -1 ) & TARGET_PAGE_MASK; ++ nr_extents = (end - begin) >> TARGET_PAGE_BITS; ++ ++ extent_start = malloc(sizeof(xen_pfn_t) * nr_extents); ++ ++ if (extent_start == NULL) { ++ fprintf(stderr, "Failed malloc on set_mm_mapping\n"); ++ return -1; ++ } ++ ++ memset(extent_start, 0, sizeof(xen_pfn_t) * nr_extents); ++ ++ for (i = 0; i < nr_extents; i++) ++ extent_start[i] = (begin + (i * TARGET_PAGE_SIZE)) >> TARGET_PAGE_BITS; ++ ++ unset_mm_mapping(xc_handle, domid, nr_extents, 0, extent_start); ++ ++ free(extent_start); ++ ++ return 0; ++} ++ + /* Compute the memory access functions */ + static void cirrus_update_memory_access(CirrusVGAState *s) + { +@@ -2538,11 +2621,39 @@ static void cirrus_update_memory_access( + + mode = s->gr[0x05] & 0x7; + if (mode < 4 || mode > 5 || ((s->gr[0x0B] & 0x4) == 0)) { ++ if (s->cirrus_lfb_addr && s->cirrus_lfb_end && !s->map_addr) { ++ void *vram_pointer, *old_vram; ++ ++ vram_pointer = set_vram_mapping(s->cirrus_lfb_addr, ++ s->cirrus_lfb_end); ++ if (!vram_pointer) ++ fprintf(stderr, "NULL vram_pointer\n"); ++ else { ++ old_vram = vga_update_vram((VGAState *)s, vram_pointer, ++ VGA_RAM_SIZE); ++ qemu_free(old_vram); ++ } ++ s->map_addr = s->cirrus_lfb_addr; ++ s->map_end = s->cirrus_lfb_end; ++ } + s->cirrus_linear_write[0] = cirrus_linear_mem_writeb; + s->cirrus_linear_write[1] = cirrus_linear_mem_writew; + s->cirrus_linear_write[2] = cirrus_linear_mem_writel; + } else { + generic_io: ++ if (s->cirrus_lfb_addr && s->cirrus_lfb_end && s->map_addr) { ++ int error; ++ void *old_vram = NULL; ++ ++ error = unset_vram_mapping(s->cirrus_lfb_addr, ++ s->cirrus_lfb_end); ++ if (!error) ++ old_vram = vga_update_vram((VGAState *)s, NULL, ++ VGA_RAM_SIZE); ++ if (old_vram) ++ munmap(old_vram, s->map_addr - s->map_end); ++ s->map_addr = s->map_end = 0; ++ } + s->cirrus_linear_write[0] = cirrus_linear_writeb; + s->cirrus_linear_write[1] = cirrus_linear_writew; + s->cirrus_linear_write[2] = cirrus_linear_writel; +@@ -3136,6 +3247,13 @@ static void cirrus_pci_lfb_map(PCIDevice + /* XXX: add byte swapping apertures */ + cpu_register_physical_memory(addr, s->vram_size, + s->cirrus_linear_io_addr); ++ s->cirrus_lfb_addr = addr; ++ s->cirrus_lfb_end = addr + VGA_RAM_SIZE; ++ ++ if (s->map_addr && (s->cirrus_lfb_addr != s->map_addr) && ++ (s->cirrus_lfb_end != s->map_end)) ++ fprintf(logfile, "cirrus vga map change while on lfb mode\n"); ++ + cpu_register_physical_memory(addr + 0x1000000, 0x400000, + s->cirrus_linear_bitblt_io_addr); + } +diff -r 62e05863ec04 hw/pc.c +--- a/hw/pc.c Mon Jun 26 15:18:40 2006 +0100 ++++ b/hw/pc.c Mon Jun 26 15:19:40 2006 +0100 +@@ -783,14 +783,14 @@ static void pc_init1(uint64_t ram_size, + if (cirrus_vga_enabled) { + if (pci_enabled) { + pci_cirrus_vga_init(pci_bus, +- ds, phys_ram_base + ram_size, ram_size, ++ ds, NULL, ram_size, + vga_ram_size); + } else { +- isa_cirrus_vga_init(ds, phys_ram_base + ram_size, ram_size, ++ isa_cirrus_vga_init(ds, NULL, ram_size, + vga_ram_size); + } + } else { +- vga_initialize(pci_bus, ds, phys_ram_base + ram_size, ram_size, ++ vga_initialize(pci_bus, ds, NULL, ram_size, + vga_ram_size, 0, 0); + } + +diff -r 62e05863ec04 hw/vga.c +--- a/hw/vga.c Mon Jun 26 15:18:40 2006 +0100 ++++ b/hw/vga.c Mon Jun 26 15:19:40 2006 +0100 +@@ -1668,6 +1668,7 @@ static void vga_map(PCIDevice *pci_dev, + } + } + ++/* when used on xen environment, the vga_ram_base is not used */ + void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base, + unsigned long vga_ram_offset, int vga_ram_size) + { +@@ -1698,7 +1699,7 @@ void vga_common_init(VGAState *s, Displa + + vga_reset(s); + +- s->vram_ptr = vga_ram_base; ++ s->vram_ptr = qemu_malloc(vga_ram_size); + s->vram_offset = vga_ram_offset; + s->vram_size = vga_ram_size; + s->ds = ds; +@@ -1808,6 +1809,31 @@ int vga_initialize(PCIBus *bus, DisplayS + #endif + } + return 0; ++} ++ ++void *vga_update_vram(VGAState *s, void *vga_ram_base, int vga_ram_size) ++{ ++ uint8_t *old_pointer; ++ ++ if (s->vram_size != vga_ram_size) { ++ fprintf(stderr, "No support to change vga_ram_size\n"); ++ return NULL; ++ } ++ ++ if (!vga_ram_base) { ++ vga_ram_base = qemu_malloc(vga_ram_size); ++ if (!vga_ram_base) { ++ fprintf(stderr, "reallocate error\n"); ++ return NULL; ++ } ++ } ++ ++ /* XXX lock needed? */ ++ memcpy(vga_ram_base, s->vram_ptr, vga_ram_size); ++ old_pointer = s->vram_ptr; ++ s->vram_ptr = vga_ram_base; ++ ++ return old_pointer; + } + + /********************************************************/ +diff -r 62e05863ec04 hw/vga_int.h +--- a/hw/vga_int.h Mon Jun 26 15:18:40 2006 +0100 ++++ b/hw/vga_int.h Mon Jun 26 15:19:40 2006 +0100 +@@ -166,5 +166,6 @@ void vga_draw_cursor_line_32(uint8_t *d1 + unsigned int color0, unsigned int color1, + unsigned int color_xor); + ++void *vga_update_vram(VGAState *s, void *vga_ram_base, int vga_ram_size); + extern const uint8_t sr_mask[8]; + extern const uint8_t gr_mask[16]; +diff -r 62e05863ec04 vl.c +--- a/vl.c Mon Jun 26 15:18:40 2006 +0100 ++++ b/vl.c Mon Jun 26 15:19:40 2006 +0100 +@@ -5147,6 +5147,78 @@ static void select_soundhw (const char * + #endif + + #define MAX_NET_CLIENTS 32 ++ ++#include ++ ++/* FIXME Flush the shadow page */ ++int unset_mm_mapping(int xc_handle, uint32_t domid, ++ unsigned long nr_pages, unsigned int address_bits, ++ xen_pfn_t *extent_start) ++{ ++ int err = 0; ++ xc_dominfo_t info; ++ ++ err = xc_domain_memory_decrease_reservation(xc_handle, domid, ++ nr_pages, 0, extent_start); ++ if (err) ++ fprintf(stderr, "Failed to decrease physmap\n"); ++ ++ xc_domain_getinfo(xc_handle, domid, 1, &info); ++ ++ if ((info.nr_pages - nr_pages) <= 0) { ++ fprintf(stderr, "unset_mm_mapping: error nr_pages\n"); ++ err = -1; ++ } ++ ++ if (xc_domain_setmaxmem(xc_handle, domid, (info.nr_pages - nr_pages) * ++ PAGE_SIZE/1024) != 0) { ++ fprintf(logfile, "set maxmem returned error %d\n", errno); ++ err = -1; ++ } ++ ++ return err; ++} ++ ++int set_mm_mapping(int xc_handle, uint32_t domid, ++ unsigned long nr_pages, unsigned int address_bits, ++ xen_pfn_t *extent_start) ++{ ++#if 0 ++ int i; ++#endif ++ xc_dominfo_t info; ++ int err = 0; ++ ++ xc_domain_getinfo(xc_handle, domid, 1, &info); ++ ++ if (xc_domain_setmaxmem(xc_handle, domid, info.max_memkb + ++ nr_pages * PAGE_SIZE/1024) != 0) { ++ fprintf(logfile, "set maxmem returned error %d\n", errno); ++ return -1; ++ } ++ ++ err = xc_domain_memory_populate_physmap(xc_handle, domid, nr_pages, 0, ++ address_bits, extent_start); ++ if (err) { ++ fprintf(stderr, "Failed to populate physmap\n"); ++ return -1; ++ } ++ ++ err = xc_domain_translate_gpfn_list(xc_handle, domid, nr_pages, ++ extent_start, extent_start); ++ if (err) { ++ fprintf(stderr, "Failed to translate gpfn list\n"); ++ return -1; ++ } ++ ++#if 0 /* Generates lots of log file output - turn on for debugging */ ++ for (i = 0; i < nr_pages; i++) ++ fprintf(stderr, "set_map result i %x result %lx\n", i, ++ extent_start[i]); ++#endif ++ ++ return 0; ++} + + int main(int argc, char **argv) + { +diff -r 62e05863ec04 vl.h +--- a/vl.h Mon Jun 26 15:18:40 2006 +0100 ++++ b/vl.h Mon Jun 26 15:19:40 2006 +0100 +@@ -136,6 +136,13 @@ extern int reset_requested; + + void main_loop_wait(int timeout); + ++int unset_mm_mapping(int xc_handle, uint32_t domid, unsigned long nr_pages, ++ unsigned int address_bits, unsigned long *extent_start); ++int set_mm_mapping(int xc_handle, uint32_t domid, unsigned long nr_pages, ++ unsigned int address_bits, unsigned long *extent_start); ++ ++extern void *shared_vram; ++ + extern FILE *logfile; + + extern int xc_handle; diff --git a/tools/ioemu/patches/support-xm-console b/tools/ioemu/patches/support-xm-console new file mode 100644 index 0000000000..b21182520d --- /dev/null +++ b/tools/ioemu/patches/support-xm-console @@ -0,0 +1,90 @@ +diff -r d08c08f8fbf3 vl.c +--- a/vl.c Mon Jun 26 15:18:25 2006 +0100 ++++ b/vl.c Mon Jun 26 15:18:37 2006 +0100 +@@ -1535,26 +1535,65 @@ CharDriverState *qemu_chr_open_stdio(voi + return chr; + } + ++int store_console_dev(int domid, char *pts) ++{ ++ int xc_handle; ++ struct xs_handle *xs; ++ char *path; ++ ++ xs = xs_daemon_open(); ++ if (xs == NULL) { ++ fprintf(logfile, "Could not contact XenStore\n"); ++ return -1; ++ } ++ ++ xc_handle = xc_interface_open(); ++ if (xc_handle == -1) { ++ fprintf(logfile, "xc_interface_open() error\n"); ++ return -1; ++ } ++ ++ path = xs_get_domain_path(xs, domid); ++ if (path == NULL) { ++ fprintf(logfile, "xs_get_domain_path() error\n"); ++ return -1; ++ } ++ path = realloc(path, strlen(path) + strlen("/console/tty") + 1); ++ if (path == NULL) { ++ fprintf(logfile, "realloc error\n"); ++ return -1; ++ } ++ strcat(path, "/console/tty"); ++ if (!xs_write(xs, XBT_NULL, path, pts, strlen(pts))) { ++ fprintf(logfile, "xs_write for console fail"); ++ return -1; ++ } ++ ++ free(path); ++ xs_daemon_close(xs); ++ close(xc_handle); ++ ++ return 0; ++} ++ + #if defined(__linux__) + CharDriverState *qemu_chr_open_pty(void) + { + struct termios tty; +- char slave_name[1024]; + int master_fd, slave_fd; + + /* Not satisfying */ +- if (openpty(&master_fd, &slave_fd, slave_name, NULL, NULL) < 0) { ++ if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) < 0) { + return NULL; + } + +- /* Disabling local echo and line-buffered output */ +- tcgetattr (master_fd, &tty); +- tty.c_lflag &= ~(ECHO|ICANON|ISIG); +- tty.c_cc[VMIN] = 1; +- tty.c_cc[VTIME] = 0; +- tcsetattr (master_fd, TCSAFLUSH, &tty); +- +- fprintf(stderr, "char device redirected to %s\n", slave_name); ++ /* Set raw attributes on the pty. */ ++ cfmakeraw(&tty); ++ tcsetattr(slave_fd, TCSAFLUSH, &tty); ++ ++ fprintf(stderr, "char device redirected to %s\n", ptsname(master_fd)); ++ store_console_dev(domid, ptsname(master_fd)); ++ + return qemu_chr_open_fd(master_fd, master_fd); + } + +@@ -5297,7 +5336,9 @@ int main(int argc, char **argv) + break; + case QEMU_OPTION_nographic: + pstrcpy(monitor_device, sizeof(monitor_device), "stdio"); +- pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "stdio"); ++ if(!strcmp(serial_devices[0], "vc")) ++ pstrcpy(serial_devices[0], sizeof(serial_devices[0]), ++ "stdio"); + nographic = 1; + break; + case QEMU_OPTION_kernel: diff --git a/tools/ioemu/patches/vnc-cleanup b/tools/ioemu/patches/vnc-cleanup new file mode 100644 index 0000000000..d44973d6fa --- /dev/null +++ b/tools/ioemu/patches/vnc-cleanup @@ -0,0 +1,80 @@ +diff -r c84300f3abc2 vnc.c +--- a/vnc.c Wed Jul 05 18:11:23 2006 +0100 ++++ b/vnc.c Thu Jul 06 14:27:28 2006 +0100 +@@ -83,13 +83,16 @@ static void vnc_dpy_update(DisplayState + static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) + { + VncState *vs = ds->opaque; +- int i; ++ uint64_t mask; + + h += y; ++ if (w != 1024) ++ mask = ((1ULL << (w / 16)) - 1) << (x / 16); ++ else ++ mask = ~(0ULL); + + for (; y < h; y++) +- for (i = 0; i < w; i += 16) +- vs->dirty_row[y] |= (1ULL << ((x + i) / 16)); ++ vs->dirty_row[y] |= mask; + } + + static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, +@@ -262,6 +265,7 @@ static void vnc_update_client(void *opaq + static void vnc_update_client(void *opaque) + { + VncState *vs = opaque; ++ int64_t now = qemu_get_clock(rt_clock); + + if (vs->need_update && vs->csock != -1) { + int y; +@@ -282,7 +286,7 @@ static void vnc_update_client(void *opaq + row = vs->ds->data; + old_row = vs->old_data; + +- for (y = 0; y < vs->height; y++) { ++ for (y = 0; y < vs->ds->height; y++) { + if (vs->dirty_row[y] & width_mask) { + int x; + char *ptr, *old_ptr; +@@ -307,10 +311,8 @@ static void vnc_update_client(void *opaq + old_row += vs->ds->linesize; + } + +- if (!has_dirty) { +- qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); +- return; +- } ++ if (!has_dirty) ++ goto out; + + /* Count rectangles */ + n_rectangles = 0; +@@ -348,7 +350,9 @@ static void vnc_update_client(void *opaq + vnc_flush(vs); + + } +- qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); ++ ++ out: ++ qemu_mod_timer(vs->timer, now + VNC_REFRESH_INTERVAL); + } + + static void vnc_timer_init(VncState *vs) +diff -r c84300f3abc2 vl.c +--- a/vl.c Wed Jul 05 18:11:23 2006 +0100 ++++ b/vl.c Thu Jul 06 14:27:28 2006 +0100 +@@ -4586,10 +4586,10 @@ void main_loop_wait(int timeout) + /* XXX: better handling of removal */ + for(ioh = first_io_handler; ioh != NULL; ioh = ioh_next) { + ioh_next = ioh->next; +- if (FD_ISSET(ioh->fd, &rfds)) { ++ if (ioh->fd_read && FD_ISSET(ioh->fd, &rfds)) { + ioh->fd_read(ioh->opaque); + } +- if (FD_ISSET(ioh->fd, &wfds)) { ++ if (ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) { + ioh->fd_write(ioh->opaque); + } + } diff --git a/tools/ioemu/patches/vnc-fixes b/tools/ioemu/patches/vnc-fixes new file mode 100644 index 0000000000..d3c8d5dccd --- /dev/null +++ b/tools/ioemu/patches/vnc-fixes @@ -0,0 +1,484 @@ +diff -r 15da4d2106fe vl.c +--- a/vl.c Thu Jul 06 14:27:28 2006 +0100 ++++ b/vl.c Thu Jul 06 20:19:49 2006 +0100 +@@ -5972,8 +5972,7 @@ int main(int argc, char **argv) + kernel_filename, kernel_cmdline, initrd_filename, + timeoffset); + +- gui_timer = qemu_new_timer(rt_clock, gui_update, NULL); +- qemu_mod_timer(gui_timer, qemu_get_clock(rt_clock)); ++ display_state.dpy_refresh(&display_state); + + #ifdef CONFIG_GDBSTUB + if (use_gdbstub) { +diff -r 15da4d2106fe vnc.c +--- a/vnc.c Thu Jul 06 14:27:28 2006 +0100 ++++ b/vnc.c Thu Jul 06 20:19:49 2006 +0100 +@@ -3,6 +3,7 @@ + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2006 Fabrice Bellard ++ * Copyright (C) 2006 Christian Limpach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal +@@ -51,7 +52,11 @@ struct VncState + int need_update; + int width; + int height; +- uint64_t dirty_row[768]; ++ uint64_t *dirty_row; /* screen regions which are possibly dirty */ ++ int dirty_pixel_shift; ++ uint64_t *update_row; /* outstanding updates */ ++ int has_update; /* there's outstanding updates in the ++ * visible area */ + char *old_data; + int depth; + int has_resize; +@@ -62,13 +67,25 @@ struct VncState + + VncReadEvent *read_handler; + size_t read_handler_expect; ++ ++ int visible_x; ++ int visible_y; ++ int visible_w; ++ int visible_h; ++ ++ int slow_client; + }; ++ ++#define DIRTY_PIXEL_BITS 64 ++#define X2DP_DOWN(vs, x) ((x) >> (vs)->dirty_pixel_shift) ++#define X2DP_UP(vs, x) \ ++ (((x) + (1ULL << (vs)->dirty_pixel_shift) - 1) >> (vs)->dirty_pixel_shift) ++#define DP2X(vs, x) ((x) << (vs)->dirty_pixel_shift) + + /* TODO + 1) Get the queue working for IO. + 2) there is some weirdness when using the -S option (the screen is grey + and not totally invalidated +- 3) resolutions > 1024 + */ + + static void vnc_write(VncState *vs, const void *data, size_t len); +@@ -77,22 +94,38 @@ static void vnc_write_u16(VncState *vs, + static void vnc_write_u16(VncState *vs, uint16_t value); + static void vnc_write_u8(VncState *vs, uint8_t value); + static void vnc_flush(VncState *vs); ++static void _vnc_update_client(void *opaque); + static void vnc_update_client(void *opaque); + static void vnc_client_read(void *opaque); +- +-static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) +-{ +- VncState *vs = ds->opaque; ++static void framebuffer_set_updated(VncState *vs, int x, int y, int w, int h); ++ ++static void set_bits_in_row(VncState *vs, uint64_t *row, ++ int x, int y, int w, int h) ++{ ++ int x1, x2; + uint64_t mask; + +- h += y; +- if (w != 1024) +- mask = ((1ULL << (w / 16)) - 1) << (x / 16); ++ if (w == 0) ++ return; ++ ++ x1 = X2DP_DOWN(vs, x); ++ x2 = X2DP_UP(vs, x + w); ++ ++ if (X2DP_UP(vs, w) != DIRTY_PIXEL_BITS) ++ mask = ((1ULL << (x2 - x1)) - 1) << x1; + else + mask = ~(0ULL); + ++ h += y; + for (; y < h; y++) +- vs->dirty_row[y] |= mask; ++ row[y] |= mask; ++} ++ ++static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) ++{ ++ VncState *vs = ds->opaque; ++ ++ set_bits_in_row(vs, vs->dirty_row, x, y, w, h); + } + + static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, +@@ -109,11 +142,15 @@ static void vnc_dpy_resize(DisplayState + static void vnc_dpy_resize(DisplayState *ds, int w, int h) + { + VncState *vs = ds->opaque; ++ int o; + + ds->data = realloc(ds->data, w * h * vs->depth); + vs->old_data = realloc(vs->old_data, w * h * vs->depth); +- +- if (ds->data == NULL || vs->old_data == NULL) { ++ vs->dirty_row = realloc(vs->dirty_row, h * sizeof(vs->dirty_row[0])); ++ vs->update_row = realloc(vs->update_row, h * sizeof(vs->dirty_row[0])); ++ ++ if (ds->data == NULL || vs->old_data == NULL || ++ vs->dirty_row == NULL || vs->update_row == NULL) { + fprintf(stderr, "vnc: memory allocation failed\n"); + exit(1); + } +@@ -131,6 +168,10 @@ static void vnc_dpy_resize(DisplayState + vs->width = ds->width; + vs->height = ds->height; + } ++ vs->dirty_pixel_shift = 0; ++ for (o = DIRTY_PIXEL_BITS; o < ds->width; o *= 2) ++ vs->dirty_pixel_shift++; ++ framebuffer_set_updated(vs, 0, 0, ds->width, ds->height); + } + + static void send_framebuffer_update_raw(VncState *vs, int x, int y, int w, int h) +@@ -215,8 +256,20 @@ static void vnc_copy(DisplayState *ds, i + int y = 0; + int pitch = ds->linesize; + VncState *vs = ds->opaque; +- +- vnc_update_client(vs); ++ int updating_client = !vs->slow_client; ++ ++ if (src_x < vs->visible_x || src_y < vs->visible_y || ++ dst_x < vs->visible_x || dst_y < vs->visible_y || ++ (src_x + w) > (vs->visible_x + vs->visible_w) || ++ (src_y + h) > (vs->visible_y + vs->visible_h) || ++ (dst_x + w) > (vs->visible_x + vs->visible_w) || ++ (dst_y + h) > (vs->visible_y + vs->visible_h)) ++ updating_client = 0; ++ ++ if (updating_client) { ++ vs->need_update = 1; ++ _vnc_update_client(vs); ++ } + + if (dst_y > src_y) { + y = h - 1; +@@ -238,31 +291,34 @@ static void vnc_copy(DisplayState *ds, i + old_row += pitch; + } + +- vnc_write_u8(vs, 0); /* msg id */ +- vnc_write_u8(vs, 0); +- vnc_write_u16(vs, 1); /* number of rects */ +- vnc_framebuffer_update(vs, dst_x, dst_y, w, h, 1); +- vnc_write_u16(vs, src_x); +- vnc_write_u16(vs, src_y); +- vnc_flush(vs); +-} +- +-static int find_dirty_height(VncState *vs, int y, int last_x, int x) ++ if (updating_client && vs->csock != -1 && !vs->has_update) { ++ vnc_write_u8(vs, 0); /* msg id */ ++ vnc_write_u8(vs, 0); ++ vnc_write_u16(vs, 1); /* number of rects */ ++ vnc_framebuffer_update(vs, dst_x, dst_y, w, h, 1); ++ vnc_write_u16(vs, src_x); ++ vnc_write_u16(vs, src_y); ++ vnc_flush(vs); ++ } else ++ framebuffer_set_updated(vs, dst_x, dst_y, w, h); ++} ++ ++static int find_update_height(VncState *vs, int y, int maxy, int last_x, int x) + { + int h; + +- for (h = 1; h < (vs->height - y); h++) { ++ for (h = 1; y + h < maxy; h++) { + int tmp_x; +- if (!(vs->dirty_row[y + h] & (1ULL << last_x))) ++ if (!(vs->update_row[y + h] & (1ULL << last_x))) + break; + for (tmp_x = last_x; tmp_x < x; tmp_x++) +- vs->dirty_row[y + h] &= ~(1ULL << tmp_x); ++ vs->update_row[y + h] &= ~(1ULL << tmp_x); + } + + return h; + } + +-static void vnc_update_client(void *opaque) ++static void _vnc_update_client(void *opaque) + { + VncState *vs = opaque; + int64_t now = qemu_get_clock(rt_clock); +@@ -274,11 +330,12 @@ static void vnc_update_client(void *opaq + uint64_t width_mask; + int n_rectangles; + int saved_offset; +- int has_dirty = 0; +- +- width_mask = (1ULL << (vs->width / 16)) - 1; +- +- if (vs->width == 1024) ++ int maxx, maxy; ++ int tile_bytes = vs->depth * DP2X(vs, 1); ++ ++ if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS)) ++ width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1; ++ else + width_mask = ~(0ULL); + + /* Walk through the dirty map and eliminate tiles that +@@ -294,16 +351,18 @@ static void vnc_update_client(void *opaq + ptr = row; + old_ptr = old_row; + +- for (x = 0; x < vs->ds->width; x += 16) { +- if (memcmp(old_ptr, ptr, 16 * vs->depth) == 0) { +- vs->dirty_row[y] &= ~(1ULL << (x / 16)); +- } else { +- has_dirty = 1; +- memcpy(old_ptr, ptr, 16 * vs->depth); ++ for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) { ++ if (vs->dirty_row[y] & (1ULL << x)) { ++ if (memcmp(old_ptr, ptr, tile_bytes)) { ++ vs->has_update = 1; ++ vs->update_row[y] |= (1ULL << x); ++ memcpy(old_ptr, ptr, tile_bytes); ++ } ++ vs->dirty_row[y] &= ~(1ULL << x); + } + +- ptr += 16 * vs->depth; +- old_ptr += 16 * vs->depth; ++ ptr += tile_bytes; ++ old_ptr += tile_bytes; + } + } + +@@ -311,7 +370,8 @@ static void vnc_update_client(void *opaq + old_row += vs->ds->linesize; + } + +- if (!has_dirty) ++ if (!vs->has_update || vs->visible_y >= vs->ds->height || ++ vs->visible_x >= vs->ds->width) + goto out; + + /* Count rectangles */ +@@ -321,38 +381,59 @@ static void vnc_update_client(void *opaq + saved_offset = vs->output.offset; + vnc_write_u16(vs, 0); + +- for (y = 0; y < vs->height; y++) { ++ maxy = vs->visible_y + vs->visible_h; ++ if (maxy > vs->ds->height) ++ maxy = vs->ds->height; ++ maxx = vs->visible_x + vs->visible_w; ++ if (maxx > vs->ds->width) ++ maxx = vs->ds->width; ++ ++ for (y = vs->visible_y; y < maxy; y++) { + int x; + int last_x = -1; +- for (x = 0; x < vs->width / 16; x++) { +- if (vs->dirty_row[y] & (1ULL << x)) { +- if (last_x == -1) { ++ for (x = X2DP_DOWN(vs, vs->visible_x); ++ x < X2DP_UP(vs, maxx); x++) { ++ if (vs->update_row[y] & (1ULL << x)) { ++ if (last_x == -1) + last_x = x; +- } +- vs->dirty_row[y] &= ~(1ULL << x); ++ vs->update_row[y] &= ~(1ULL << x); + } else { + if (last_x != -1) { +- int h = find_dirty_height(vs, y, last_x, x); +- send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h); ++ int h = find_update_height(vs, y, maxy, last_x, x); ++ send_framebuffer_update(vs, DP2X(vs, last_x), y, ++ DP2X(vs, (x - last_x)), h); + n_rectangles++; + } + last_x = -1; + } + } + if (last_x != -1) { +- int h = find_dirty_height(vs, y, last_x, x); +- send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h); ++ int h = find_update_height(vs, y, maxy, last_x, x); ++ send_framebuffer_update(vs, DP2X(vs, last_x), y, ++ DP2X(vs, (x - last_x)), h); + n_rectangles++; + } + } + vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF; + vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF; ++ ++ vs->has_update = 0; ++ vs->need_update = 0; + vnc_flush(vs); +- +- } ++ vs->slow_client = 0; ++ } else ++ vs->slow_client = 1; + + out: + qemu_mod_timer(vs->timer, now + VNC_REFRESH_INTERVAL); ++} ++ ++static void vnc_update_client(void *opaque) ++{ ++ VncState *vs = opaque; ++ ++ vs->ds->dpy_refresh(vs->ds); ++ _vnc_update_client(vs); + } + + static void vnc_timer_init(VncState *vs) +@@ -365,8 +446,6 @@ static void vnc_timer_init(VncState *vs) + + static void vnc_dpy_refresh(DisplayState *ds) + { +- VncState *vs = ds->opaque; +- vnc_timer_init(vs); + vga_hw_update(); + } + +@@ -402,7 +481,7 @@ static char *buffer_end(Buffer *buffer) + + static void buffer_reset(Buffer *buffer) + { +- buffer->offset = 0; ++ buffer->offset = 0; + } + + static void buffer_append(Buffer *buffer, const void *data, size_t len) +@@ -443,12 +522,12 @@ static void vnc_client_write(void *opaqu + if (!ret) + return; + +- memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret)); ++ memmove(vs->output.buffer, vs->output.buffer + ret, ++ vs->output.offset - ret); + vs->output.offset -= ret; + +- if (vs->output.offset == 0) { ++ if (vs->output.offset == 0) + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); +- } + } + + static void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting) +@@ -480,11 +559,11 @@ static void vnc_client_read(void *opaque + return; + + if (!ret) { +- memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len)); ++ memmove(vs->input.buffer, vs->input.buffer + len, ++ vs->input.offset - len); + vs->input.offset -= len; +- } else { ++ } else + vs->read_handler_expect = ret; +- } + } + } + +@@ -492,9 +571,9 @@ static void vnc_write(VncState *vs, cons + { + buffer_reserve(&vs->output, len); + +- if (buffer_empty(&vs->output)) { +- qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); +- } ++ if (buffer_empty(&vs->output)) ++ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, ++ vnc_client_write, vs); + + buffer_append(&vs->output, data, len); + } +@@ -616,24 +695,25 @@ static void key_event(VncState *vs, int + do_key_event(vs, down, sym); + } + ++static void framebuffer_set_updated(VncState *vs, int x, int y, int w, int h) ++{ ++ ++ set_bits_in_row(vs, vs->update_row, x, y, w, h); ++ ++ vs->has_update = 1; ++} ++ + static void framebuffer_update_request(VncState *vs, int incremental, + int x_position, int y_position, + int w, int h) + { +- int i; + vs->need_update = 1; +- if (!incremental) { +- char *old_row = vs->old_data + y_position * vs->ds->linesize; +- +- for (i = 0; i < h; i++) { +- vs->dirty_row[y_position + i] = (1ULL << (vs->ds->width / 16)) - 1; +- if (vs->ds->width == 1024) { +- vs->dirty_row[y_position + i] = ~(0ULL); +- } +- memset(old_row, 42, vs->ds->width * vs->depth); +- old_row += vs->ds->linesize; +- } +- } ++ if (!incremental) ++ framebuffer_set_updated(vs, x_position, y_position, w, h); ++ vs->visible_x = x_position; ++ vs->visible_y = y_position; ++ vs->visible_w = w; ++ vs->visible_h = h; + } + + static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) +@@ -690,8 +770,6 @@ static void set_pixel_format(VncState *v + vnc_client_error(vs); + + vnc_dpy_resize(vs->ds, vs->ds->width, vs->ds->height); +- memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row)); +- memset(vs->old_data, 42, vs->ds->linesize * vs->ds->height); + + vga_hw_invalidate(); + vga_hw_update(); +@@ -848,11 +926,11 @@ static void vnc_listen_read(void *opaque + vnc_write(vs, "RFB 003.003\n", 12); + vnc_flush(vs); + vnc_read_when(vs, protocol_version, 12); +- memset(vs->old_data, 0, vs->ds->linesize * vs->ds->height); +- memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row)); ++ framebuffer_set_updated(vs, 0, 0, vs->ds->width, vs->ds->height); + vs->has_resize = 0; + vs->has_hextile = 0; + vs->ds->dpy_copy = NULL; ++ vnc_timer_init(vs); + } + } + +@@ -909,17 +987,15 @@ void vnc_display_init(DisplayState *ds, + exit(1); + } + +- ret = qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs); +- if (ret == -1) { ++ ret = qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, ++ NULL, vs); ++ if (ret == -1) + exit(1); +- } + + vs->ds->data = NULL; + vs->ds->dpy_update = vnc_dpy_update; + vs->ds->dpy_resize = vnc_dpy_resize; + vs->ds->dpy_refresh = vnc_dpy_refresh; + +- memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row)); +- + vnc_dpy_resize(vs->ds, 640, 400); + } diff --git a/tools/ioemu/patches/xen-build b/tools/ioemu/patches/xen-build new file mode 100644 index 0000000000..0816211eea --- /dev/null +++ b/tools/ioemu/patches/xen-build @@ -0,0 +1,231 @@ +Index: ioemu/Makefile +=================================================================== +--- ioemu.orig/Makefile 2006-07-12 10:39:09.278608692 +0100 ++++ ioemu/Makefile 2006-07-12 10:46:21.003128750 +0100 +@@ -1,6 +1,9 @@ ++XEN_ROOT=../.. ++include $(XEN_ROOT)/tools/Rules.mk ++ + include config-host.mak + +-CFLAGS=-Wall -O2 -g -fno-strict-aliasing -I. ++CFLAGS+=-Wall -O2 -g -fno-strict-aliasing -I. + ifdef CONFIG_DARWIN + CFLAGS+= -mdynamic-no-pic + endif +@@ -17,7 +20,7 @@ + DOCS= + endif + +-all: dyngen$(EXESUF) $(TOOLS) $(DOCS) ++all: $(DOCS) + for d in $(TARGET_DIRS); do \ + $(MAKE) -C $$d $@ || exit 1 ; \ + done +@@ -57,12 +60,12 @@ + + install: all $(if $(BUILD_DOCS),install-doc) + mkdir -p "$(DESTDIR)$(bindir)" +- $(INSTALL) -m 755 -s $(TOOLS) "$(DESTDIR)$(bindir)" +- mkdir -p "$(DESTDIR)$(datadir)" +- for x in bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \ +- video.x proll.elf linux_boot.bin; do \ +- $(INSTALL) -m 644 $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \ +- done ++# $(INSTALL) -m 755 -s $(TOOLS) "$(DESTDIR)$(bindir)" ++# mkdir -p "$(DESTDIR)$(datadir)" ++# for x in bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \ ++# video.x proll.elf linux_boot.bin; do \ ++# $(INSTALL) -m 644 $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \ ++# done + ifndef CONFIG_WIN32 + mkdir -p "$(DESTDIR)$(datadir)/keymaps" + for x in $(KEYMAPS); do \ +@@ -96,11 +99,11 @@ + texi2dvi $< + + qemu.1: qemu-doc.texi +- $(SRC_PATH)/texi2pod.pl $< qemu.pod ++ perl -w $(SRC_PATH)/texi2pod.pl $< qemu.pod + pod2man --section=1 --center=" " --release=" " qemu.pod > $@ + + qemu-img.1: qemu-img.texi +- $(SRC_PATH)/texi2pod.pl $< qemu-img.pod ++ perl -w $(SRC_PATH)/texi2pod.pl $< qemu-img.pod + pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@ + + FILE=qemu-$(shell cat VERSION) +Index: ioemu/Makefile.target +=================================================================== +--- ioemu.orig/Makefile.target 2006-07-12 10:39:09.279608582 +0100 ++++ ioemu/Makefile.target 2006-07-12 11:32:51.034101952 +0100 +@@ -1,5 +1,8 @@ + include config.mak + ++XEN_ROOT=../../.. ++include $(XEN_ROOT)/tools/Rules.mk ++ + TARGET_BASE_ARCH:=$(TARGET_ARCH) + ifeq ($(TARGET_ARCH), x86_64) + TARGET_BASE_ARCH:=i386 +@@ -10,14 +13,21 @@ + ifeq ($(TARGET_ARCH), sparc64) + TARGET_BASE_ARCH:=sparc + endif +-TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH) ++TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH)$(TARGET_SUB) + VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/audio + DEFINES=-I. -I.. -I$(TARGET_PATH) -I$(SRC_PATH) ++DEFINES+= -I$(XEN_ROOT)/tools/libxc ++DEFINES+= -I$(XEN_ROOT)/tools/xenstore + ifdef CONFIG_USER_ONLY + VPATH+=:$(SRC_PATH)/linux-user + DEFINES+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ARCH) + endif +-CFLAGS=-Wall -O2 -g -fno-strict-aliasing ++CFLAGS+=-Wall -O2 -g -fno-strict-aliasing ++SSE2 := $(call test-gcc-flag,$(CC),-msse2) ++ifeq ($(SSE2),-msse2) ++CFLAGS += -DUSE_SSE2=1 -msse2 ++endif ++CFLAGS+= $(LOCAL_CFLAGS) + #CFLAGS+=-Werror + LDFLAGS=-g + LIBS= +@@ -155,6 +165,9 @@ + + DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE + LIBS+=-lm ++LIBS+=-L../../libxc -lxenctrl -lxenguest ++LIBS+=-L../../xenstore -lxenstore ++LIBS+=-lpthread + ifndef CONFIG_USER_ONLY + LIBS+=-lz + endif +@@ -264,7 +277,7 @@ + all: $(PROGS) + + $(QEMU_USER): $(OBJS) +- $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) ++ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) + ifeq ($(ARCH),alpha) + # Mark as 32 bit binary, i. e. it will be mapped into the low 31 bit of + # the address space (31 bit so sign extending doesn't matter) +@@ -490,10 +503,16 @@ + clean: + rm -f *.o *.a *~ $(PROGS) gen-op.h opc.h op.h nwfpe/*.o slirp/*.o fpu/*.o + ++distclean: clean ++ rm -rf config.mak config.h ++ + install: all ++ mkdir -p "$(DESTDIR)$(bindir)" "$(DESTDIR)$(configdir)" + ifneq ($(PROGS),) + $(INSTALL) -m 755 -s $(PROGS) "$(DESTDIR)$(bindir)" + endif ++ install -m 755 $(TARGET_PATH)/qemu-dm.debug "$(DESTDIR)$(bindir)" ++ install -m 755 $(TARGET_PATH)/qemu-ifup "$(DESTDIR)$(configdir)" + + ifneq ($(wildcard .depend),) + include .depend +Index: ioemu/configure +=================================================================== +--- ioemu.orig/configure 2006-07-12 10:39:09.280608472 +0100 ++++ ioemu/configure 2006-07-12 11:32:51.034101952 +0100 +@@ -18,8 +18,8 @@ + + # default parameters + prefix="" +-interp_prefix="/usr/gnemul/qemu-%M" + static="no" ++libdir="lib" + cross_prefix="" + cc="gcc" + host_cc="gcc" +@@ -65,6 +65,7 @@ + ;; + x86_64|amd64) + cpu="x86_64" ++ libdir="lib64" + ;; + *) + cpu="unknown" +@@ -91,7 +92,7 @@ + kernel_path="" + cocoa="no" + check_gfx="yes" +-check_gcc="yes" ++check_gcc="no" + softmmu="yes" + user="no" + build_docs="no" +@@ -366,6 +367,8 @@ + exit 1 + fi + ++kqemu="no" ++ + if test -z "$cross_prefix" ; then + + # --- +@@ -491,14 +494,16 @@ + datadir="$prefix" + docdir="$prefix" + bindir="$prefix" ++configdir="" + else + if test -z "$prefix" ; then + prefix="/usr/local" + fi + mandir="$prefix/share/man" +-datadir="$prefix/share/qemu" ++datadir="$prefix/share/xen/qemu" + docdir="$prefix/share/doc/qemu" +-bindir="$prefix/bin" ++bindir="$prefix/$libdir/xen/bin" ++configdir="/etc/xen" + fi + + echo "Install prefix $prefix" +@@ -567,6 +572,8 @@ + echo "mandir=$mandir" >> $config_mak + echo "datadir=$datadir" >> $config_mak + echo "docdir=$docdir" >> $config_mak ++echo "configdir=$configdir" >> $config_mak ++echo "LIBDIR=$libdir" >> $config_mak + echo "#define CONFIG_QEMU_SHAREDIR \"$datadir\"" >> $config_h + echo "MAKE=$make" >> $config_mak + echo "INSTALL=$install" >> $config_mak +@@ -748,7 +755,7 @@ + # don't use ln -sf as not all "ln -sf" over write the file/link + # + rm -f $target_dir/Makefile +-ln -s $source_path/Makefile.target $target_dir/Makefile ++ln -s ../Makefile.target $target_dir/Makefile + + + echo "# Automatically generated by configure - do not modify" > $config_mak +@@ -761,6 +768,12 @@ + interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_cpu/g"` + echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix1\"" >> $config_h + ++target_sub= ++if expr $target : '.*-dm' > /dev/null ; then ++ target_sub=-dm ++fi ++echo "TARGET_SUB=${target_sub}" >> $config_mak ++ + if test "$target_cpu" = "i386" ; then + echo "TARGET_ARCH=i386" >> $config_mak + echo "#define TARGET_ARCH \"i386\"" >> $config_h +@@ -823,6 +836,10 @@ + echo "#define CONFIG_USER_ONLY 1" >> $config_h + fi + ++if expr $target : '.*-dm' > /dev/null ; then ++ echo "#define CONFIG_DM 1" >> $config_h ++fi ++ + if test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" ; then + echo "CONFIG_SOFTFLOAT=yes" >> $config_mak + echo "#define CONFIG_SOFTFLOAT 1" >> $config_h diff --git a/tools/ioemu/patches/xen-domain-name b/tools/ioemu/patches/xen-domain-name new file mode 100644 index 0000000000..b6b90603c1 --- /dev/null +++ b/tools/ioemu/patches/xen-domain-name @@ -0,0 +1,78 @@ +Index: ioemu/sdl.c +=================================================================== +--- ioemu.orig/sdl.c 2006-07-12 11:33:54.665109493 +0100 ++++ ioemu/sdl.c 2006-07-12 11:35:01.450735012 +0100 +@@ -268,14 +268,14 @@ + static void sdl_update_caption(void) + { + char buf[1024]; +- strcpy(buf, "QEMU"); ++ strcpy(buf, domain_name); + if (!vm_running) { + strcat(buf, " [Stopped]"); + } + if (gui_grab) { + strcat(buf, " - Press Ctrl-Alt to exit grab"); + } +- SDL_WM_SetCaption(buf, "QEMU"); ++ SDL_WM_SetCaption(buf, domain_name); + } + + static void sdl_hide_cursor(void) +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-07-12 11:35:01.094779608 +0100 ++++ ioemu/vl.c 2006-07-12 11:35:01.453734636 +0100 +@@ -159,6 +159,8 @@ + #define MAX_CPUS 1 + #endif + ++char domain_name[1024] = { 'H','V', 'M', 'X', 'E', 'N', '-'}; ++ + /***********************************************************/ + /* x86 ISA bus support */ + +@@ -4698,6 +4700,7 @@ + "-s wait gdb connection to port %d\n" + "-p port change gdb connection port\n" + "-l item1,... output log to %s (use -d ? for a list of log items)\n" ++ "-domain-name domain name that we're serving\n" + "-hdachs c,h,s[,t] force hard disk 0 physical geometry and the optional BIOS\n" + " translation (t=none or lba) (usually qemu can guess them)\n" + "-L path set the directory for the BIOS and VGA BIOS\n" +@@ -4787,6 +4790,7 @@ + QEMU_OPTION_g, + QEMU_OPTION_std_vga, + QEMU_OPTION_monitor, ++ QEMU_OPTION_domainname, + QEMU_OPTION_serial, + QEMU_OPTION_parallel, + QEMU_OPTION_loadvm, +@@ -4860,6 +4864,7 @@ + { "localtime", 0, QEMU_OPTION_localtime }, + { "std-vga", 0, QEMU_OPTION_std_vga }, + { "monitor", 1, QEMU_OPTION_monitor }, ++ { "domain-name", 1, QEMU_OPTION_domainname }, + { "serial", 1, QEMU_OPTION_serial }, + { "parallel", 1, QEMU_OPTION_parallel }, + { "loadvm", HAS_ARG, QEMU_OPTION_loadvm }, +@@ -5483,6 +5488,9 @@ + exit(1); + } + break; ++ case QEMU_OPTION_domainname: ++ strncat(domain_name, optarg, sizeof(domain_name) - 20); ++ break; + } + } + } +Index: ioemu/vl.h +=================================================================== +--- ioemu.orig/vl.h 2006-07-12 11:35:00.955797021 +0100 ++++ ioemu/vl.h 2006-07-12 11:35:01.454734511 +0100 +@@ -1094,4 +1094,5 @@ + + void kqemu_record_dump(void); + ++extern char domain_name[]; + #endif /* VL_H */ diff --git a/tools/ioemu/patches/xen-domid b/tools/ioemu/patches/xen-domid new file mode 100644 index 0000000000..d1d6f9273e --- /dev/null +++ b/tools/ioemu/patches/xen-domid @@ -0,0 +1,48 @@ +diff -r 03705e837ce8 vl.c +--- a/vl.c Tue May 30 14:10:44 2006 +0100 ++++ b/vl.c Tue May 30 14:11:16 2006 +0100 +@@ -160,6 +160,7 @@ int vnc_display = -1; + #endif + + char domain_name[1024] = { 'H','V', 'M', 'X', 'E', 'N', '-'}; ++extern int domid; + + /***********************************************************/ + /* x86 ISA bus support */ +@@ -4700,6 +4701,7 @@ void help(void) + "-s wait gdb connection to port %d\n" + "-p port change gdb connection port\n" + "-l item1,... output log to %s (use -d ? for a list of log items)\n" ++ "-d domain domain that we're serving\n" + "-domain-name domain name that we're serving\n" + "-hdachs c,h,s[,t] force hard disk 0 physical geometry and the optional BIOS\n" + " translation (t=none or lba) (usually qemu can guess them)\n" +@@ -4803,6 +4805,8 @@ enum { + QEMU_OPTION_usbdevice, + QEMU_OPTION_smp, + QEMU_OPTION_vnc, ++ ++ QEMU_OPTION_d, + }; + + typedef struct QEMUOption { +@@ -4878,6 +4882,8 @@ const QEMUOption qemu_options[] = { + /* temporary options */ + { "usb", 0, QEMU_OPTION_usb }, + { "cirrusvga", 0, QEMU_OPTION_cirrusvga }, ++ ++ { "d", HAS_ARG, QEMU_OPTION_d }, + { NULL }, + }; + +@@ -5491,6 +5497,10 @@ int main(int argc, char **argv) + case QEMU_OPTION_domainname: + strncat(domain_name, optarg, sizeof(domain_name) - 20); + break; ++ case QEMU_OPTION_d: ++ domid = atoi(optarg); ++ fprintf(logfile, "domid: %d\n", domid); ++ break; + } + } + } diff --git a/tools/ioemu/patches/xen-mm b/tools/ioemu/patches/xen-mm new file mode 100644 index 0000000000..feb537ed56 --- /dev/null +++ b/tools/ioemu/patches/xen-mm @@ -0,0 +1,109 @@ +diff -r f36cde91babe hw/pc.c +--- a/hw/pc.c Mon Jun 26 15:16:50 2006 +0100 ++++ b/hw/pc.c Mon Jun 26 15:18:19 2006 +0100 +@@ -639,7 +639,9 @@ static void pc_init1(uint64_t ram_size, + } + + /* allocate RAM */ ++#ifndef CONFIG_DM /* HVM domain owns memory */ + cpu_register_physical_memory(0, ram_size, 0); ++#endif + + /* BIOS load */ + bios_offset = ram_size + vga_ram_size; +@@ -671,8 +673,10 @@ static void pc_init1(uint64_t ram_size, + ret = load_image(buf, phys_ram_base + vga_bios_offset); + + /* setup basic memory access */ ++#ifndef CONFIG_DM /* HVM domain owns memory */ + cpu_register_physical_memory(0xc0000, 0x10000, + vga_bios_offset | IO_MEM_ROM); ++#endif + + /* map the last 128KB of the BIOS in ISA space */ + isa_bios_size = bios_size; +diff -r f36cde91babe vl.c +--- a/vl.c Mon Jun 26 15:16:50 2006 +0100 ++++ b/vl.c Mon Jun 26 15:18:19 2006 +0100 +@@ -158,6 +158,8 @@ int vnc_display = -1; + #else + #define MAX_CPUS 1 + #endif ++ ++int xc_handle; + + char domain_name[1024] = { 'H','V', 'M', 'X', 'E', 'N', '-'}; + extern int domid; +@@ -5105,6 +5107,9 @@ int main(int argc, char **argv) + QEMUMachine *machine; + char usb_devices[MAX_VM_USB_PORTS][128]; + int usb_devices_index; ++ unsigned long nr_pages; ++ xen_pfn_t *page_array; ++ extern void *shared_page; + + char qemu_dm_logfilename[64]; + +@@ -5341,11 +5346,13 @@ int main(int argc, char **argv) + ram_size = atol(optarg) * 1024 * 1024; + if (ram_size <= 0) + help(); ++#ifndef CONFIG_DM + if (ram_size > PHYS_RAM_MAX_SIZE) { + fprintf(stderr, "qemu: at most %d MB RAM can be simulated\n", + PHYS_RAM_MAX_SIZE / (1024 * 1024)); + exit(1); + } ++#endif /* !CONFIG_DM */ + break; + case QEMU_OPTION_l: + { +@@ -5557,6 +5564,39 @@ int main(int argc, char **argv) + /* init the memory */ + phys_ram_size = ram_size + vga_ram_size + bios_size; + ++#ifdef CONFIG_DM ++ ++ nr_pages = ram_size/PAGE_SIZE; ++ xc_handle = xc_interface_open(); ++ ++ page_array = (xen_pfn_t *)malloc(nr_pages * sizeof(xen_pfn_t)); ++ if (page_array == NULL) { ++ fprintf(logfile, "malloc returned error %d\n", errno); ++ exit(-1); ++ } ++ ++ if (xc_get_pfn_list(xc_handle, domid, page_array, nr_pages) != nr_pages) { ++ fprintf(logfile, "xc_get_pfn_list returned error %d\n", errno); ++ exit(-1); ++ } ++ ++ phys_ram_base = xc_map_foreign_batch(xc_handle, domid, ++ PROT_READ|PROT_WRITE, page_array, ++ nr_pages - 1); ++ if (phys_ram_base == 0) { ++ fprintf(logfile, "xc_map_foreign_batch returned error %d\n", errno); ++ exit(-1); ++ } ++ ++ shared_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, ++ PROT_READ|PROT_WRITE, ++ page_array[nr_pages - 1]); ++ ++ fprintf(logfile, "shared page at pfn:%lx, mfn: %"PRIx64"\n", nr_pages - 1, ++ (uint64_t)(page_array[nr_pages - 1])); ++ ++#else /* !CONFIG_DM */ ++ + #ifdef CONFIG_SOFTMMU + phys_ram_base = qemu_vmalloc(phys_ram_size); + if (!phys_ram_base) { +@@ -5596,6 +5636,8 @@ int main(int argc, char **argv) + } + } + #endif ++ ++#endif /* !CONFIG_DM */ + + /* we always create the cdrom drive, even if no disk is there */ + bdrv_init(); diff --git a/tools/ioemu/patches/xen-network b/tools/ioemu/patches/xen-network new file mode 100644 index 0000000000..e4c44d6607 --- /dev/null +++ b/tools/ioemu/patches/xen-network @@ -0,0 +1,63 @@ +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-07-12 11:35:01.753697055 +0100 ++++ ioemu/vl.c 2006-07-12 11:35:02.126650330 +0100 +@@ -89,6 +89,7 @@ + #include "exec-all.h" + + #define DEFAULT_NETWORK_SCRIPT "/etc/xen/qemu-ifup" ++#define DEFAULT_BRIDGE "xenbr0" + + //#define DEBUG_UNUSED_IOPORT + //#define DEBUG_IOPORT +@@ -2621,11 +2622,11 @@ + #endif + + static int net_tap_init(VLANState *vlan, const char *ifname1, +- const char *setup_script) ++ const char *setup_script, const char *bridge) + { + TAPState *s; + int pid, status, fd; +- char *args[3]; ++ char *args[4]; + char **parg; + char ifname[128]; + +@@ -2647,6 +2648,7 @@ + parg = args; + *parg++ = (char *)setup_script; + *parg++ = ifname; ++ *parg++ = (char *)bridge; + *parg++ = NULL; + execv(setup_script, args); + _exit(1); +@@ -3201,6 +3203,7 @@ + if (!strcmp(device, "tap")) { + char ifname[64]; + char setup_script[1024]; ++ char bridge[16]; + int fd; + if (get_param_value(buf, sizeof(buf), "fd", p) > 0) { + fd = strtol(buf, NULL, 0); +@@ -3212,7 +3215,10 @@ + if (get_param_value(setup_script, sizeof(setup_script), "script", p) == 0) { + pstrcpy(setup_script, sizeof(setup_script), DEFAULT_NETWORK_SCRIPT); + } +- ret = net_tap_init(vlan, ifname, setup_script); ++ if (get_param_value(bridge, sizeof(bridge), "bridge", p) == 0) { ++ pstrcpy(bridge, sizeof(bridge), DEFAULT_BRIDGE); ++ } ++ ret = net_tap_init(vlan, ifname, setup_script, bridge); + } + } else + #endif +@@ -4671,7 +4677,7 @@ + "-net tap[,vlan=n],ifname=name\n" + " connect the host TAP network interface to VLAN 'n'\n" + #else +- "-net tap[,vlan=n][,fd=h][,ifname=name][,script=file]\n" ++ "-net tap[,vlan=n][,fd=h][,ifname=name][,script=file][,bridge=br]\n" + " connect the host TAP network interface to VLAN 'n' and use\n" + " the network script 'file' (default=%s);\n" + " use 'fd=h' to connect to an already opened TAP interface\n" diff --git a/tools/ioemu/pc-bios/CVS/Entries b/tools/ioemu/pc-bios/CVS/Entries new file mode 100644 index 0000000000..75847c2486 --- /dev/null +++ b/tools/ioemu/pc-bios/CVS/Entries @@ -0,0 +1,15 @@ +/Makefile/1.1/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/README/1.7/Thu May 25 12:38:52 2006//Trelease_0_8_1 +/bios.bin/1.13/Thu May 25 18:22:37 2006/-kb/Trelease_0_8_1 +/bios.diff/1.11/Thu May 25 18:22:37 2006//Trelease_0_8_1 +/linux_boot.S/1.1/Wed May 17 14:47:01 2006//Trelease_0_8_1 +/linux_boot.bin/1.1/Wed May 17 14:47:01 2006/-kb/Trelease_0_8_1 +/ohw.diff/1.2/Thu Jul 7 22:38:00 2005//Trelease_0_8_1 +/ppc_rom.bin/1.6/Thu May 25 12:39:00 2006/-kb/Trelease_0_8_1 +/proll.elf/1.5/Thu May 25 18:22:38 2006/-kb/Trelease_0_8_1 +/proll.patch/1.6/Thu May 25 18:22:38 2006//Trelease_0_8_1 +/vgabios-cirrus.bin/1.6/Thu May 25 18:22:38 2006/-kb/Trelease_0_8_1 +/vgabios.bin/1.5/Thu May 25 18:22:39 2006/-kb/Trelease_0_8_1 +/vgabios.diff/1.2/Thu May 25 18:22:39 2006//Trelease_0_8_1 +/video.x/1.1/Sun Jul 3 14:00:51 2005/-kb/Trelease_0_8_1 +D diff --git a/tools/ioemu/pc-bios/CVS/Repository b/tools/ioemu/pc-bios/CVS/Repository new file mode 100644 index 0000000000..de93946755 --- /dev/null +++ b/tools/ioemu/pc-bios/CVS/Repository @@ -0,0 +1 @@ +qemu/pc-bios diff --git a/tools/ioemu/pc-bios/CVS/Root b/tools/ioemu/pc-bios/CVS/Root new file mode 100644 index 0000000000..e8a9815e6a --- /dev/null +++ b/tools/ioemu/pc-bios/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.savannah.nongnu.org:/sources/qemu diff --git a/tools/ioemu/pc-bios/CVS/Tag b/tools/ioemu/pc-bios/CVS/Tag new file mode 100644 index 0000000000..31c251a4bf --- /dev/null +++ b/tools/ioemu/pc-bios/CVS/Tag @@ -0,0 +1 @@ +Nrelease_0_8_1 diff --git a/tools/ioemu/pc-bios/Makefile b/tools/ioemu/pc-bios/Makefile new file mode 100644 index 0000000000..7ae0ff02a0 --- /dev/null +++ b/tools/ioemu/pc-bios/Makefile @@ -0,0 +1,24 @@ +# +# NOTE: only compilable with x86 cross compile tools +# +include ../config-host.mak + +DEFINES= + +TARGETS= +ifeq ($(ARCH),i386) +TARGETS+=linux_boot.bin +endif + +all: $(TARGETS) + +linux_boot.bin: linux_boot.o + ld --oformat binary -Ttext 0 -o $@ $< + chmod a-x $@ + +%.o: %.S + $(CC) $(DEFINES) -c -o $@ $< + +clean: + rm -f $(TARGETS) *.o *~ + diff --git a/tools/ioemu/pc-bios/README b/tools/ioemu/pc-bios/README new file mode 100644 index 0000000000..5e61a28fc1 --- /dev/null +++ b/tools/ioemu/pc-bios/README @@ -0,0 +1,17 @@ +- The PC BIOS comes from the Bochs project + (http://bochs.sourceforge.net/). A patch from bios.diff was applied. + +- The VGA BIOS and the Cirrus VGA BIOS come from the LGPL VGA bios + project (http://www.nongnu.org/vgabios/). + +- The PowerPC Open Hack'Ware Open Firmware Compatible BIOS is + available at http://perso.magic.fr/l_indien/OpenHackWare/index.htm. + +- Proll is a GPL'd boot PROM for Sparc JavaStations + (http://people.redhat.com/zaitcev/linux/). + Applying proll.patch allows circumventing some bugs and enables + faster kernel load through a hack. + +- video.x is a PowerMac NDRV compatible driver for a VGA frame + buffer. It comes from the Mac-on-Linux project + (http://www.maconlinux.org/). diff --git a/tools/ioemu/pc-bios/bios.diff b/tools/ioemu/pc-bios/bios.diff new file mode 100644 index 0000000000..647025b889 --- /dev/null +++ b/tools/ioemu/pc-bios/bios.diff @@ -0,0 +1,140 @@ +Index: apmbios.S +=================================================================== +RCS file: /cvsroot/bochs/bochs/bios/apmbios.S,v +retrieving revision 1.4 +diff -u -w -r1.4 apmbios.S +--- apmbios.S 26 Dec 2005 10:35:51 -0000 1.4 ++++ apmbios.S 28 Apr 2006 22:41:19 -0000 +@@ -225,6 +225,7 @@ + APMSYM(05): + cmp al, #0x05 + jne APMSYM(07) ++ sti + hlt + jmp APMSYM(ok) + +Index: rombios.c +=================================================================== +RCS file: /cvsroot/bochs/bochs/bios/rombios.c,v +retrieving revision 1.160 +diff -u -w -r1.160 rombios.c +--- rombios.c 25 Jan 2006 17:51:49 -0000 1.160 ++++ rombios.c 28 Apr 2006 22:41:21 -0000 +@@ -1816,6 +1816,7 @@ + { + printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ", + BIOS_BUILD_DATE, bios_cvs_version_string); ++#if 0 + printf( + #ifdef BX_APM + "apmbios " +@@ -1827,6 +1828,9 @@ + "eltorito " + #endif + "\n\n"); ++#else ++ printf("apmbios pcibios eltorito \n\n"); ++#endif + } + + //-------------------------------------------------------------------------- +@@ -8713,6 +8717,7 @@ + mov al, #0x80 + bios32_end: + popf ++ and dword ptr[esp+4],0xfffffffc ;; reset CS.RPL for kqemu + retf + + .align 16 +@@ -8823,17 +8828,17 @@ + pci_pro_fail: + pop edi + pop esi +- sti + popf + stc ++ and dword ptr[esp+4],0xfffffffc ;; reset CS.RPL for kqemu + retf + pci_pro_ok: + xor ah, ah + pop edi + pop esi +- sti + popf + clc ++ and dword ptr[esp+4],0xfffffffc ;; reset CS.RPL for kqemu + retf + + pci_pro_select_reg: +@@ -8971,7 +8976,7 @@ + jmp pci_real_ok + pci_real_f0d: ;; write configuration dword + cmp al, #0x0d +- jne pci_real_unknown ++ jne pci_real_f0e + call pci_real_select_reg + push dx + mov dx, #0x0cfc +@@ -8979,6 +8984,46 @@ + out dx, eax + pop dx + jmp pci_real_ok ++pci_real_f0e: ;; get irq routing options ++ cmp al, #0x0e ++ jne pci_real_unknown ++ SEG ES ++ cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start ++ jb pci_real_too_small ++ SEG ES ++ mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start ++ pushf ++ push ds ++ push es ++ push cx ++ push si ++ push di ++ cld ++ mov si, #pci_routing_table_structure_start ++ push cs ++ pop ds ++ SEG ES ++ mov cx, [di+2] ++ SEG ES ++ mov es, [di+4] ++ mov di, cx ++ mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start ++ rep ++ movsb ++ pop di ++ pop si ++ pop cx ++ pop es ++ pop ds ++ popf ++ mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used ++ jmp pci_real_ok ++pci_real_too_small: ++ SEG ES ++ mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start ++ mov ah, #0x89 ++ jmp pci_real_fail ++ + pci_real_unknown: + mov ah, #0x81 + pci_real_fail: +@@ -9019,6 +9064,7 @@ + dw 0,0 ;; Miniport data + db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved + db 0x07 ;; checksum ++pci_routing_table_structure_start: + ;; first slot entry PCI-to-ISA (embedded) + db 0 ;; pci bus number + db 0x08 ;; pci device number (bit 7-3) +@@ -9097,6 +9143,7 @@ + dw 0xdef8 ;; IRQ bitmap INTD# + db 5 ;; physical slot (0 = embedded) + db 0 ;; reserved ++pci_routing_table_structure_end: + + pci_irq_list: + db 11, 10, 9, 5; diff --git a/tools/ioemu/pc-bios/linux_boot.S b/tools/ioemu/pc-bios/linux_boot.S new file mode 100644 index 0000000000..22fcd4be80 --- /dev/null +++ b/tools/ioemu/pc-bios/linux_boot.S @@ -0,0 +1,29 @@ +/* + * QEMU Boot sector to launch a preloaded Linux kernel + * Copyright (c) 2004 Fabrice Bellard + */ + +#define LOAD_SEG 0x9000 + +.code16 +.text + .globl _start + +_start: + cli + cld + mov $LOAD_SEG, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + mov $0x8ffe, %sp + ljmp $LOAD_SEG + 0x20, $0 + +1: + .fill 510 - (1b - _start), 1, 0 + + /* boot sector signature */ + .byte 0x55 + .byte 0xaa diff --git a/tools/ioemu/pc-bios/ohw.diff b/tools/ioemu/pc-bios/ohw.diff new file mode 100644 index 0000000000..4fb542274d --- /dev/null +++ b/tools/ioemu/pc-bios/ohw.diff @@ -0,0 +1,1843 @@ +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/bios.h OpenHackWare-release-0.4/src/bios.h +--- OpenHackWare-release-0.4.org/src/bios.h 2005-04-06 23:20:22.000000000 +0200 ++++ OpenHackWare-release-0.4/src/bios.h 2005-07-07 01:10:20.000000000 +0200 +@@ -64,6 +64,7 @@ + ARCH_CHRP, + ARCH_MAC99, + ARCH_POP, ++ ARCH_HEATHROW, + }; + + /* Hardware definition(s) */ +@@ -174,6 +175,7 @@ + int bd_ioctl (bloc_device_t *bd, int func, void *args); + uint32_t bd_seclen (bloc_device_t *bd); + void bd_close (bloc_device_t *bd); ++void bd_reset_all(void); + uint32_t bd_seclen (bloc_device_t *bd); + uint32_t bd_maxbloc (bloc_device_t *bd); + void bd_sect2CHS (bloc_device_t *bd, uint32_t secnum, +@@ -183,12 +185,12 @@ + part_t *bd_probe (int boot_device); + bloc_device_t *bd_get (int device); + void bd_put (bloc_device_t *bd); +-void bd_set_boot_part (bloc_device_t *bd, part_t *partition); ++void bd_set_boot_part (bloc_device_t *bd, part_t *partition, int partnum); + part_t **_bd_parts (bloc_device_t *bd); + + void ide_pci_pc_register (uint32_t io_base0, uint32_t io_base1, + uint32_t io_base2, uint32_t io_base3, +- void *OF_private); ++ void *OF_private0, void *OF_private1); + void ide_pci_pmac_register (uint32_t io_base0, uint32_t io_base1, + void *OF_private); + +@@ -399,17 +401,23 @@ + uint16_t min_grant, uint16_t max_latency); + void OF_finalize_pci_host (void *dev, int first_bus, int nb_busses); + void OF_finalize_pci_device (void *dev, uint8_t bus, uint8_t devfn, +- uint32_t *regions, uint32_t *sizes); ++ uint32_t *regions, uint32_t *sizes, ++ int irq_line); + void OF_finalize_pci_macio (void *dev, uint32_t base_address, uint32_t size, + void *private_data); ++void OF_finalize_pci_ide (void *dev, ++ uint32_t io_base0, uint32_t io_base1, ++ uint32_t io_base2, uint32_t io_base3); + int OF_register_bus (const unsigned char *name, uint32_t address, + const unsigned char *type); + int OF_register_serial (const unsigned char *bus, const unsigned char *name, + uint32_t io_base, int irq); + int OF_register_stdio (const unsigned char *dev_in, + const unsigned char *dev_out); +-void OF_vga_register (const unsigned char *name, uint32_t address, +- int width, int height, int depth); ++void OF_vga_register (const unsigned char *name, unused uint32_t address, ++ int width, int height, int depth, ++ unsigned long vga_bios_addr, ++ unsigned long vga_bios_size); + void *OF_blockdev_register (void *parent, void *private, + const unsigned char *type, + const unsigned char *name, int devnum, +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/bloc.c OpenHackWare-release-0.4/src/bloc.c +--- OpenHackWare-release-0.4.org/src/bloc.c 2005-04-06 23:21:00.000000000 +0200 ++++ OpenHackWare-release-0.4/src/bloc.c 2005-07-08 00:28:26.000000000 +0200 +@@ -55,6 +55,7 @@ + /* Partitions */ + part_t *parts, *bparts; + part_t *boot_part; ++ int bpartnum; + /* Chain */ + bloc_device_t *next; + }; +@@ -66,6 +67,7 @@ + + static int ide_initialize (bloc_device_t *bd, int device); + static int ide_read_sector (bloc_device_t *bd, void *buffer, int secnum); ++static int ide_reset (bloc_device_t *bd); + + static int mem_initialize (bloc_device_t *bd, int device); + static int mem_read_sector (bloc_device_t *bd, void *buffer, int secnum); +@@ -212,6 +214,17 @@ + { + } + ++void bd_reset_all(void) ++{ ++ bloc_device_t *bd; ++ for (bd = bd_list; bd != NULL; bd = bd->next) { ++ if (bd->init == &ide_initialize) { ++ /* reset IDE drive because Darwin wants all IDE devices to be reset */ ++ ide_reset(bd); ++ } ++ } ++} ++ + uint32_t bd_seclen (bloc_device_t *bd) + { + return bd->seclen; +@@ -223,10 +236,12 @@ + } + + /* XXX: to be suppressed */ +-void bd_set_boot_part (bloc_device_t *bd, part_t *partition) ++void bd_set_boot_part (bloc_device_t *bd, part_t *partition, int partnum) + { ++ dprintf("%s: part %p (%p) %d\n", __func__, partition, bd->boot_part, partnum); + if (bd->boot_part == NULL) { + bd->boot_part = partition; ++ bd->bpartnum = partnum; + } + } + +@@ -240,6 +255,13 @@ + return &bd->bparts; + } + ++void bd_set_boot_device (bloc_device_t *bd) ++{ ++#if defined (USE_OPENFIRMWARE) ++ OF_blockdev_set_boot_device(bd->OF_private, bd->bpartnum, "\\\\ofwboot"); ++#endif ++} ++ + part_t *bd_probe (int boot_device) + { + char devices[] = { /*'a', 'b',*/ 'c', 'd', 'e', 'f', 'm', '\0', }; +@@ -272,9 +294,7 @@ + tmp = part_probe(bd, force_raw); + if (boot_device == bd->device) { + boot_part = tmp; +-#if defined (USE_OPENFIRMWARE) +- OF_blockdev_set_boot_device(bd->OF_private, 2, "\\\\ofwboot"); +-#endif ++ bd_set_boot_device(bd); + } + } + +@@ -717,34 +737,29 @@ + /* IDE PCI access for pc */ + static uint8_t ide_pci_port_read (bloc_device_t *bd, int port) + { +- eieio(); +- +- return *(uint8_t *)(bd->io_base + port); ++ uint8_t value; ++ value = inb(bd->io_base + port); ++ return value; + } + + static void ide_pci_port_write (bloc_device_t *bd, int port, uint8_t value) + { +- *(uint8_t *)(bd->io_base + port) = value; +- eieio(); ++ outb(bd->io_base + port, value); + } + + static uint32_t ide_pci_data_readl (bloc_device_t *bd) + { +- eieio(); +- +- return *((uint32_t *)bd->io_base); ++ return inl(bd->io_base); + } + + static void ide_pci_data_writel (bloc_device_t *bd, uint32_t val) + { +- *(uint32_t *)(bd->io_base) = val; +- eieio(); ++ outl(bd->io_base, val); + } + + static void ide_pci_control_write (bloc_device_t *bd, uint32_t val) + { +- *((uint8_t *)bd->tmp) = val; +- eieio(); ++ outb(bd->tmp + 2, val); + } + + static ide_ops_t ide_pci_pc_ops = { +@@ -761,7 +776,7 @@ + + void ide_pci_pc_register (uint32_t io_base0, uint32_t io_base1, + uint32_t io_base2, uint32_t io_base3, +- unused void *OF_private) ++ void *OF_private0, void *OF_private1) + { + if (ide_pci_ops == NULL) { + ide_pci_ops = malloc(sizeof(ide_ops_t)); +@@ -770,19 +785,19 @@ + memcpy(ide_pci_ops, &ide_pci_pc_ops, sizeof(ide_ops_t)); + } + if ((io_base0 != 0 || io_base1 != 0) && +- ide_pci_ops->base[0] == 0 && ide_pci_ops->base[1] == 0) { ++ ide_pci_ops->base[0] == 0 && ide_pci_ops->base[2] == 0) { + ide_pci_ops->base[0] = io_base0; +- ide_pci_ops->base[1] = io_base1; ++ ide_pci_ops->base[2] = io_base1; + #ifdef USE_OPENFIRMWARE +- ide_pci_ops->OF_private[0] = OF_private; ++ ide_pci_ops->OF_private[0] = OF_private0; + #endif + } + if ((io_base2 != 0 || io_base3 != 0) && +- ide_pci_ops->base[2] == 0 && ide_pci_ops->base[3] == 0) { +- ide_pci_ops->base[2] = io_base2; ++ ide_pci_ops->base[1] == 0 && ide_pci_ops->base[3] == 0) { ++ ide_pci_ops->base[1] = io_base2; + ide_pci_ops->base[3] = io_base3; + #ifdef USE_OPENFIRMWARE +- ide_pci_ops->OF_private[1] = OF_private; ++ ide_pci_ops->OF_private[1] = OF_private1; + #endif + } + } +@@ -935,6 +950,8 @@ + } + + static void atapi_pad_req (void *buffer, int len); ++static void atapi_make_req (bloc_device_t *bd, uint32_t *buffer, ++ int maxlen); + static int atapi_read_sector (bloc_device_t *bd, void *buffer, int secnum); + + static int ide_initialize (bloc_device_t *bd, int device) +@@ -1035,9 +1052,7 @@ + DPRINTF("INQUIRY\n"); + len = spc_inquiry_req(&atapi_buffer, 36); + atapi_pad_req(&atapi_buffer, len); +- ide_port_write(bd, 0x07, 0xA0); +- for (i = 0; i < 3; i++) +- ide_data_writel(bd, ldswap32(&atapi_buffer[i])); ++ atapi_make_req(bd, atapi_buffer, 36); + status = ide_port_read(bd, 0x07); + if (status != 0x48) { + ERROR("ATAPI INQUIRY : status %0x != 0x48\n", status); +@@ -1053,9 +1068,7 @@ + DPRINTF("READ_CAPACITY\n"); + len = mmc_read_capacity_req(&atapi_buffer); + atapi_pad_req(&atapi_buffer, len); +- ide_port_write(bd, 0x07, 0xA0); +- for (i = 0; i < 3; i++) +- ide_data_writel(bd, ldswap32(&atapi_buffer[i])); ++ atapi_make_req(bd, atapi_buffer, 8); + status = ide_port_read(bd, 0x07); + if (status != 0x48) { + ERROR("ATAPI READ_CAPACITY : status %0x != 0x48\n", status); +@@ -1105,6 +1118,22 @@ + memset(p + len, 0, 12 - len); + } + ++static void atapi_make_req (bloc_device_t *bd, uint32_t *buffer, ++ int maxlen) ++{ ++ int i; ++ /* select drive */ ++ if (bd->drv == 0) ++ ide_port_write(bd, 0x06, 0x40); ++ else ++ ide_port_write(bd, 0x06, 0x50); ++ ide_port_write(bd, 0x04, maxlen & 0xff); ++ ide_port_write(bd, 0x05, (maxlen >> 8) & 0xff); ++ ide_port_write(bd, 0x07, 0xA0); ++ for (i = 0; i < 3; i++) ++ ide_data_writel(bd, ldswap32(&buffer[i])); ++} ++ + static int atapi_read_sector (bloc_device_t *bd, void *buffer, int secnum) + { + uint32_t atapi_buffer[4]; +@@ -1112,16 +1141,9 @@ + uint32_t status, value; + int i, len; + +- /* select drive */ +- if (bd->drv == 0) +- ide_port_write(bd, 0x06, 0x40); +- else +- ide_port_write(bd, 0x06, 0x50); + len = mmc_read12_req(atapi_buffer, secnum, 1); + atapi_pad_req(&atapi_buffer, len); +- ide_port_write(bd, 0x07, 0xA0); +- for (i = 0; i < 3; i++) +- ide_data_writel(bd, ldswap32(&atapi_buffer[i])); ++ atapi_make_req(bd, atapi_buffer, bd->seclen); + status = ide_port_read(bd, 0x07); + if (status != 0x48) { + ERROR("ATAPI READ12 : status %0x != 0x48\n", status); +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/apple.c OpenHackWare-release-0.4/src/libpart/apple.c +--- OpenHackWare-release-0.4.org/src/libpart/apple.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/libpart/apple.c 2005-07-03 16:17:41.000000000 +0200 +@@ -199,14 +199,18 @@ + if (len == 0) { + /* Place holder. Skip it */ + DPRINTF("%s placeholder part\t%d\n", __func__, i); ++ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY; ++ part_register(bd, part, name, i); + } else if (strncmp("Apple_Void", type, 32) == 0) { + /* Void partition. Skip it */ + DPRINTF("%s Void part\t%d [%s]\n", __func__, i, type); ++ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY; ++ part_register(bd, part, name, i); + } else if (strncmp("Apple_Free", type, 32) == 0) { + /* Free space. Skip it */ + DPRINTF("%s Free part (%d)\n", __func__, i); + part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY; +- part_register(bd, part, name); ++ part_register(bd, part, name, i); + } else if (strncmp("Apple_partition_map", type, 32) == 0 || + strncmp("Apple_Partition_Map", type, 32) == 0 + #if 0 // Is this really used or is it just a mistake ? +@@ -226,7 +230,7 @@ + */ + } + part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY; +- part_register(bd, part, name); ++ part_register(bd, part, name, i); + } else if (strncmp("Apple_Driver", type, 32) == 0 || + strncmp("Apple_Driver43", type, 32) == 0 || + strncmp("Apple_Driver43_CD", type, 32) == 0 || +@@ -236,8 +240,12 @@ + strncmp("Apple_Driver_IOKit", type, 32) == 0) { + /* Drivers. don't care for now */ + DPRINTF("%s Drivers part\t%d [%s]\n", __func__, i, type); ++ part->flags = PART_TYPE_APPLE | PART_FLAG_DRIVER; ++ part_register(bd, part, name, i); + } else if (strncmp("Apple_Patches", type, 32) == 0) { + /* Patches: don't care for now */ ++ part->flags = PART_TYPE_APPLE | PART_FLAG_PATCH; ++ part_register(bd, part, name, i); + DPRINTF("%s Patches part\t%d [%s]\n", __func__, i, type); + } else if (strncmp("Apple_HFS", type, 32) == 0 || + strncmp("Apple_MFS", type, 32) == 0 || +@@ -256,9 +264,8 @@ + count = partmap->bloc_cnt * HFS_BLOCSIZE; + if (partmap->boot_size == 0 || partmap->boot_load == 0) { + printf("Not a bootable partition %d %d (%p %p)\n", +- partmap->boot_size, partmap->boot_load,boot_part, part); +- if (boot_part == NULL) +- boot_part = part; ++ partmap->boot_size, partmap->boot_load, ++ boot_part, part); + part->flags = PART_TYPE_APPLE | PART_FLAG_FS; + } else { + part->boot_start.bloc = partmap->boot_start; +@@ -278,8 +285,8 @@ + boot_part = part; + part->flags = PART_TYPE_APPLE | PART_FLAG_FS | PART_FLAG_BOOT; + } +- printf("Partition: %d %s st %0x size %0x", +- i, name, partmap->start_bloc, partmap->bloc_cnt); ++ printf("Partition: %d '%s' '%s' st %0x size %0x", ++ i, name, type, partmap->start_bloc, partmap->bloc_cnt); + #ifndef DEBUG + printf("\n"); + #endif +@@ -290,11 +297,13 @@ + part->boot_load, part->boot_entry); + DPRINTF(" load %0x entry %0x %0x\n", + partmap->boot_load2, partmap->boot_entry2, HFS_BLOCSIZE); +- part_register(bd, part, name); ++ part_register(bd, part, name, i); + } else { + memcpy(tmp, type, 32); + tmp[32] = '\0'; + ERROR("Unknown partition type [%s]\n", tmp); ++ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY; ++ part_register(bd, part, name, i); + } + } + error: +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/core.c OpenHackWare-release-0.4/src/libpart/core.c +--- OpenHackWare-release-0.4.org/src/libpart/core.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/libpart/core.c 2005-07-03 16:17:41.000000000 +0200 +@@ -126,7 +126,7 @@ + } + + int part_register (bloc_device_t *bd, part_t *partition, +- const unsigned char *name) ++ const unsigned char *name, int partnum) + { + part_t **cur; + +@@ -134,6 +134,7 @@ + partition->bd = bd; + partition->next = NULL; + partition->name = strdup(name); ++ partition->partnum = partnum; + for (cur = _bd_parts(bd); *cur != NULL; cur = &(*cur)->next) + continue; + *cur = partition; +@@ -141,29 +142,15 @@ + return 0; + } + +-static inline int set_boot_part (bloc_device_t *bd, int partnum) +-{ +- part_t *cur; +- +- cur = part_get(bd, partnum); +- if (cur == NULL) +- return -1; +- bd_set_boot_part(bd, cur); +- +- return 0; +-} +- + part_t *part_get (bloc_device_t *bd, int partnum) + { + part_t **listp, *cur; +- int i; + + listp = _bd_parts(bd); +- cur = *listp; +- for (i = 0; i != partnum; i++) { +- if (cur == NULL) ++ ++ for (cur = *listp; cur != NULL; cur = cur->next) { ++ if (cur->partnum == partnum) + break; +- cur = cur->next; + } + + return cur; +@@ -192,17 +179,20 @@ + part_set_blocsize(bd, part, 512); + part->bd = bd; + part->flags = PART_TYPE_RAW | PART_FLAG_BOOT; +- part_register(bd, part, "Raw"); ++ part_register(bd, part, "Raw", 0); + + return part; + } + ++bloc_device_t *part_get_bd (part_t *part) ++{ ++ return part->bd; ++} ++ + part_t *part_probe (bloc_device_t *bd, int set_raw) + { +- part_t *part0, *boot_part, **cur; ++ part_t *part0 = NULL, *boot_part, **cur; + +- /* Register the 0 partition: raw partition containing the whole disk */ +- part0 = part_get_raw(bd); + /* Try to find a valid boot partition */ + boot_part = Apple_probe_partitions(bd); + if (boot_part == NULL) { +@@ -210,10 +200,13 @@ + if (boot_part == NULL && arch == ARCH_PREP) + boot_part = PREP_find_partition(bd); + if (boot_part == NULL && set_raw != 0) { +- boot_part = part0; +- set_boot_part(bd, 0); ++ dprintf("Use bloc device as raw partition\n"); + } + } ++ if (_bd_parts(bd) == NULL) { ++ /* Register the 0 partition: raw partition containing the whole disk */ ++ part0 = part_get_raw(bd); ++ } + /* Probe filesystem on each found partition */ + for (cur = _bd_parts(bd); *cur != NULL; cur = &(*cur)->next) { + const unsigned char *map, *type; +@@ -248,23 +241,28 @@ + type = "unknown"; + break; + } +- DPRINTF("Probe filesystem on %s %s partition '%s' %s\n", ++ dprintf("Probe filesystem on %s %s partition '%s' %s %p\n", + type, map, (*cur)->name, +- ((*cur)->flags) & PART_FLAG_BOOT ? "(bootable)" : ""); ++ ((*cur)->flags) & PART_FLAG_BOOT ? "(bootable)" : "", *cur); + if (((*cur)->flags) & PART_FLAG_FS) { + if (((*cur)->flags) & PART_FLAG_BOOT) + (*cur)->fs = fs_probe(*cur, 1); + else + (*cur)->fs = fs_probe(*cur, 0); ++ } else if (((*cur)->flags) & PART_TYPE_RAW) { ++ (*cur)->fs = fs_probe(*cur, 2); + } else { + (*cur)->fs = fs_probe(*cur, 2); + } +- if (((*cur)->flags) & PART_FLAG_BOOT) { +- bd_set_boot_part(bd, *cur); + fs_get_bootfile((*cur)->fs); ++ if (((*cur)->flags) & PART_FLAG_BOOT) { ++ dprintf("Partition is bootable (%d)\n", (*cur)->partnum); ++ bd_set_boot_part(bd, *cur, (*cur)->partnum); ++ if (boot_part == NULL) ++ boot_part = *cur; + } + } +- DPRINTF("Boot partition: %p %p %p %p\n", boot_part, boot_part->fs, ++ dprintf("Boot partition: %p %p %p %p\n", boot_part, boot_part->fs, + part_fs(boot_part), part0); + + return boot_part; +@@ -279,6 +277,7 @@ + part->boot_size.offset = 0; + part->boot_load = 0; + part->boot_entry = 0; ++ part->flags |= PART_FLAG_BOOT; + + return 0; + } +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/isofs.c OpenHackWare-release-0.4/src/libpart/isofs.c +--- OpenHackWare-release-0.4.org/src/libpart/isofs.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/libpart/isofs.c 2005-07-03 16:17:41.000000000 +0200 +@@ -242,7 +242,7 @@ + part->boot_start.bloc, part->boot_size.bloc, + part->boot_load, part->boot_entry); + part->flags = PART_TYPE_ISO9660 | PART_FLAG_BOOT; +- part_register(bd, part, name); ++ part_register(bd, part, name, i + 1); + fs_raw_set_bootfile(part, part->boot_start.bloc, + part->boot_start.offset, + part->boot_size.bloc, +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/libpart.h OpenHackWare-release-0.4/src/libpart/libpart.h +--- OpenHackWare-release-0.4.org/src/libpart/libpart.h 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/libpart/libpart.h 2005-07-03 16:17:41.000000000 +0200 +@@ -30,6 +30,7 @@ + + struct part_t { + bloc_device_t *bd; ++ int partnum; + uint32_t start; /* Partition first bloc */ + uint32_t size; /* Partition size, in blocs */ + uint32_t spb; +@@ -54,7 +55,7 @@ + }; + + int part_register (bloc_device_t *bd, part_t *partition, +- const unsigned char *name); ++ const unsigned char *name, int partnum); + void part_set_blocsize (bloc_device_t *bd, part_t *part, uint32_t blocsize); + void part_private_set (part_t *part, void *private); + void *part_private_get (part_t *part); +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/prep.c OpenHackWare-release-0.4/src/libpart/prep.c +--- OpenHackWare-release-0.4.org/src/libpart/prep.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/libpart/prep.c 2005-07-03 16:17:41.000000000 +0200 +@@ -164,7 +164,7 @@ + part->boot_load = 0; + part->boot_entry = boot_offset - part->bloc_size; + part->flags = PART_TYPE_PREP | PART_FLAG_BOOT; +- part_register(bd, part, "PREP boot"); ++ part_register(bd, part, "PREP boot", i); + fs_raw_set_bootfile(part, part->boot_start.bloc, + part->boot_start.offset, + part->boot_size.bloc, +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/main.c OpenHackWare-release-0.4/src/main.c +--- OpenHackWare-release-0.4.org/src/main.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/main.c 2005-06-07 23:48:39.000000000 +0200 +@@ -364,20 +364,24 @@ + void *load_base, *load_entry, *last_alloc, *load_end; + uint32_t memsize, boot_image_size, cmdline_size, ramdisk_size; + uint32_t boot_base, boot_nb; +- int boot_device; ++ int boot_device, i; ++ static const uint32_t isa_base_tab[3] = { ++ 0x80000000, /* PREP */ ++ 0xFE000000, /* Grackle (Heathrow) */ ++ 0xF2000000, /* UniNorth (Mac99) */ ++ }; + + /* Retrieve NVRAM configuration */ +- nvram_retry: ++ for(i = 0; i < 3; i++) { ++ isa_io_base = isa_base_tab[i]; + nvram = NVRAM_get_config(&memsize, &boot_device, + &boot_image, &boot_image_size, + &cmdline, &cmdline_size, + &ramdisk, &ramdisk_size); +- if (nvram == NULL) { +- /* Retry with another isa_io_base */ +- if (isa_io_base == 0x80000000) { +- isa_io_base = 0xF2000000; +- goto nvram_retry; ++ if (nvram) ++ break; + } ++ if (i == 3) { + ERROR("Unable to load configuration from NVRAM. Aborting...\n"); + return -1; + } +@@ -402,7 +406,7 @@ + cpu_name = CPU_get_name(pvr); + OF_register_cpu(cpu_name, 0, pvr, + 200 * 1000 * 1000, 200 * 1000 * 1000, +- 100 * 1000 * 1000, 10 * 1000 * 1000, ++ 100 * 1000 * 1000, 100 * 1000 * 1000, + 0x0092); + } + OF_register_memory(memsize, 512 * 1024 /* TOFIX */); +@@ -433,9 +437,12 @@ + vga_puts(copyright); + vga_puts("\n"); + ++#if 0 + /* QEMU is quite incoherent: d is cdrom, not second drive */ ++ /* XXX: should probe CD-ROM position */ + if (boot_device == 'd') + boot_device = 'e'; ++#endif + /* Open boot device */ + boot_part = bd_probe(boot_device); + if (boot_device == 'm') { +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/nvram.c OpenHackWare-release-0.4/src/nvram.c +--- OpenHackWare-release-0.4.org/src/nvram.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/nvram.c 2005-06-04 23:44:03.000000000 +0200 +@@ -334,6 +334,7 @@ + ret = NVRAM_chrp_format(nvram); + break; + case ARCH_MAC99: ++ case ARCH_HEATHROW: /* XXX: may be incorrect */ + ret = NVRAM_mac99_format(nvram); + break; + case ARCH_POP: +@@ -409,13 +410,12 @@ + arch = ARCH_MAC99; + } else if (strcmp(sign, "POP") == 0) { + arch = ARCH_POP; ++ } else if (strcmp(sign, "HEATHROW") == 0) { ++ arch = ARCH_HEATHROW; + } else { + ERROR("Unknown PPC architecture: '%s'\n", sign); + return NULL; + } +- /* HACK */ +- if (arch == ARCH_CHRP) +- arch = ARCH_MAC99; + lword = NVRAM_get_lword(nvram, 0x30); + *RAM_size = lword; + byte = NVRAM_get_byte(nvram, 0x34); +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/of.c OpenHackWare-release-0.4/src/of.c +--- OpenHackWare-release-0.4.org/src/of.c 2005-04-06 23:17:26.000000000 +0200 ++++ OpenHackWare-release-0.4/src/of.c 2005-07-07 23:30:08.000000000 +0200 +@@ -489,7 +489,7 @@ + ERROR("%s can't alloc new node '%s' name\n", __func__, name); + return NULL; + } +- new->prop_address = OF_prop_int_new(env, new, "address", address); ++ new->prop_address = OF_prop_int_new(env, new, "unit-address", address); + if (new->prop_address == NULL) { + free(new->prop_name->value); + free(new->prop_name); +@@ -1017,6 +1017,33 @@ + string, strlen(string) + 1); + } + ++/* convert '\1' char to '\0' */ ++static OF_prop_t *OF_prop_string_new1 (OF_env_t *env, OF_node_t *node, ++ const unsigned char *name, ++ const unsigned char *string) ++{ ++ int len, i; ++ OF_prop_t *ret; ++ unsigned char *str; ++ ++ if (strchr(string, '\1') == NULL) { ++ return OF_prop_string_new(env, node, name, string); ++ } else { ++ len = strlen(string) + 1; ++ str = malloc(len); ++ if (!str) ++ return NULL; ++ memcpy(str, string, len); ++ for(i = 0; i < len; i++) ++ if (str[i] == '\1') ++ str[i] = '\0'; ++ ret = OF_property_new(env, node, name, ++ str, len); ++ free(str); ++ return ret; ++ } ++} ++ + __attribute__ (( section (".OpenFirmware") )) + static OF_prop_t *OF_prop_int_new (OF_env_t *env, OF_node_t *node, + const unsigned char *name, uint32_t value) +@@ -1421,15 +1448,12 @@ + __attribute__ (( section (".OpenFirmware") )) + int OF_init (void) + { +- const unsigned char compat_str[] = + #if 0 + "PowerMac3,1\0MacRISC\0Power Macintosh\0"; + "PowerMac1,2\0MacRISC\0Power Macintosh\0"; + "AAPL,PowerMac G3\0PowerMac G3\0MacRISC\0Power Macintosh\0"; + "AAPL,PowerMac3,0\0MacRISC\0Power Macintosh\0"; + "AAPL,Gossamer\0MacRISC\0Power Macintosh\0"; +-#else +- "AAPL,PowerMac G3\0PowerMac G3\0MacRISC\0Power Macintosh\0"; + #endif + OF_env_t *OF_env; + OF_node_t *als, *opt, *chs, *pks; +@@ -1455,15 +1479,21 @@ + return -1; + } + OF_prop_string_new(OF_env, OF_node_root, "device_type", "bootrom"); +-#if 0 +- OF_prop_string_new(OF_env, OF_node_root, +- "model", "PPC Open Hack'Ware " BIOS_VERSION); +-#else ++ if (arch == ARCH_HEATHROW) { ++ const unsigned char compat_str[] = ++ "PowerMac1,1\0MacRISC\0Power Macintosh"; ++ OF_property_new(OF_env, OF_node_root, "compatible", ++ compat_str, sizeof(compat_str)); + OF_prop_string_new(OF_env, OF_node_root, +- "model", compat_str); +-#endif ++ "model", "Power Macintosh"); ++ } else { ++ const unsigned char compat_str[] = ++ "PowerMac3,1\0MacRISC\0Power Macintosh"; + OF_property_new(OF_env, OF_node_root, "compatible", + compat_str, sizeof(compat_str)); ++ OF_prop_string_new(OF_env, OF_node_root, ++ "model", "PowerMac3,1"); ++ } + #if 0 + OF_prop_string_new(OF_env, OF_node_root, "copyright", copyright); + #else +@@ -1561,14 +1591,15 @@ + range.size = 0x00800000; + OF_property_new(OF_env, rom, "ranges", &range, sizeof(OF_range_t)); + OF_prop_int_new(OF_env, rom, "#address-cells", 1); ++ + /* "/rom/boot-rom@fff00000" node */ +- brom = OF_node_new(OF_env, OF_node_root, "boot-rom", 0xfff00000); ++ brom = OF_node_new(OF_env, rom, "boot-rom", 0xfff00000); + if (brom == NULL) { + ERROR("Cannot create 'boot-rom'\n"); + return -1; + } + regs.address = 0xFFF00000; +- regs.size = 0x00010000; ++ regs.size = 0x00100000; + OF_property_new(OF_env, brom, "reg", ®s, sizeof(OF_regprop_t)); + OF_prop_string_new(OF_env, brom, "write-characteristic", "flash"); + OF_prop_string_new(OF_env, brom, "BootROM-build-date", +@@ -1577,7 +1608,7 @@ + OF_prop_string_new(OF_env, brom, "copyright", copyright); + OF_prop_string_new(OF_env, brom, "model", BIOS_str); + OF_prop_int_new(OF_env, brom, "result", 0); +-#if 0 ++#if 1 + { + /* Hack taken 'as-is' from PearPC */ + unsigned char info[] = { +@@ -1596,7 +1627,9 @@ + OF_node_put(OF_env, brom); + OF_node_put(OF_env, rom); + } ++#if 0 + /* From here, hardcoded hacks to get a Mac-like machine */ ++ /* XXX: Core99 does not seem to like this NVRAM tree */ + /* "/nvram@fff04000" node */ + { + OF_regprop_t regs; +@@ -1617,6 +1650,7 @@ + OF_prop_int_new(OF_env, chs, "nvram", OF_pack_handle(OF_env, nvr)); + OF_node_put(OF_env, nvr); + } ++#endif + /* "/pseudo-hid" : hid emulation as Apple does */ + { + OF_node_t *hid; +@@ -1663,7 +1697,27 @@ + } + OF_node_put(OF_env, hid); + } ++ if (arch == ARCH_MAC99) { ++ OF_node_t *unin; ++ OF_regprop_t regs; + ++ unin = OF_node_new(OF_env, OF_node_root, ++ "uni-n", 0xf8000000); ++ if (unin == NULL) { ++ ERROR("Cannot create 'uni-n'\n"); ++ return -1; ++ } ++ OF_prop_string_new(OF_env, unin, "device-type", "memory-controller"); ++ OF_prop_string_new(OF_env, unin, "model", "AAPL,UniNorth"); ++ OF_prop_string_new(OF_env, unin, "compatible", "uni-north"); ++ regs.address = 0xf8000000; ++ regs.size = 0x01000000; ++ OF_property_new(OF_env, unin, "reg", ®s, sizeof(regs)); ++ OF_prop_int_new(OF_env, unin, "#address-cells", 1); ++ OF_prop_int_new(OF_env, unin, "#size-cells", 1); ++ OF_prop_int_new(OF_env, unin, "device-rev", 3); ++ OF_node_put(OF_env, unin); ++ } + + #if 1 /* This is mandatory for claim to work + * but I don't know where it should really be (in cpu ?) +@@ -1693,7 +1747,9 @@ + + /* "/options/boot-args" node */ + { +- const unsigned char *args = "-v rootdev cdrom"; ++ // const unsigned char *args = "-v rootdev cdrom"; ++ //const unsigned char *args = "-v io=0xffffffff"; ++ const unsigned char *args = "-v"; + /* Ask MacOS X to print debug messages */ + // OF_prop_string_new(OF_env, chs, "machargs", args); + // OF_prop_string_new(OF_env, opt, "boot-command", args); +@@ -2013,17 +2069,17 @@ + OF_prop_int_new(OF_env, node, "min-grant", min_grant); + OF_prop_int_new(OF_env, node, "max-latency", max_latency); + if (dev->type != NULL) +- OF_prop_string_new(OF_env, node, "device_type", dev->type); ++ OF_prop_string_new1(OF_env, node, "device_type", dev->type); + if (dev->compat != NULL) +- OF_prop_string_new(OF_env, node, "compatible", dev->compat); ++ OF_prop_string_new1(OF_env, node, "compatible", dev->compat); + if (dev->model != NULL) +- OF_prop_string_new(OF_env, node, "model", dev->model); ++ OF_prop_string_new1(OF_env, node, "model", dev->model); + if (dev->acells != 0) + OF_prop_int_new(OF_env, node, "#address-cells", dev->acells); + if (dev->scells != 0) +- OF_prop_int_new(OF_env, node, "#interrupt-cells", dev->acells); ++ OF_prop_int_new(OF_env, node, "#size-cells", dev->scells); + if (dev->icells != 0) +- OF_prop_int_new(OF_env, node, "#size-cells", dev->acells); ++ OF_prop_int_new(OF_env, node, "#interrupt-cells", dev->icells); + dprintf("Done %p %p\n", parent, node); + + return node; +@@ -2040,8 +2096,9 @@ + OF_env_t *OF_env; + pci_range_t ranges[3]; + OF_regprop_t regs[1]; +- OF_node_t *pci_host; ++ OF_node_t *pci_host, *als; + int nranges; ++ unsigned char buffer[OF_NAMELEN_MAX]; + + OF_env = OF_env_main; + dprintf("register PCI host '%s' '%s' '%s' '%s'\n", +@@ -2052,6 +2109,17 @@ + ERROR("Cannot create pci host\n"); + return NULL; + } ++ ++ als = OF_node_get(OF_env, "aliases"); ++ if (als == NULL) { ++ ERROR("Cannot get 'aliases'\n"); ++ return NULL; ++ } ++ sprintf(buffer, "/%s", dev->name); ++ OF_prop_string_set(OF_env, als, "pci", buffer); ++ OF_node_put(OF_env, als); ++ ++ + regs[0].address = cfg_base; + regs[0].size = cfg_len; + OF_property_new(OF_env, pci_host, "reg", regs, sizeof(OF_regprop_t)); +@@ -2136,6 +2204,11 @@ + return pci_dev; + } + ++/* XXX: suppress that, used for interrupt map init */ ++OF_node_t *pci_host_node; ++uint32_t pci_host_interrupt_map[7 * 32]; ++int pci_host_interrupt_map_len = 0; ++ + void OF_finalize_pci_host (void *dev, int first_bus, int nb_busses) + { + OF_env_t *OF_env; +@@ -2145,10 +2218,12 @@ + regs[0].address = first_bus; + regs[0].size = nb_busses; + OF_property_new(OF_env, dev, "bus-range", regs, sizeof(OF_regprop_t)); ++ pci_host_node = dev; + } + + void OF_finalize_pci_device (void *dev, uint8_t bus, uint8_t devfn, +- uint32_t *regions, uint32_t *sizes) ++ uint32_t *regions, uint32_t *sizes, ++ int irq_line) + { + OF_env_t *OF_env; + pci_reg_prop_t pregs[6], rregs[6]; +@@ -2156,6 +2231,7 @@ + int i, j, k; + + OF_env = OF_env_main; ++ /* XXX: only useful for VGA card in fact */ + if (regions[0] != 0x00000000) + OF_prop_int_set(OF_env, dev, "address", regions[0] & ~0x0000000F); + for (i = 0, j = 0, k = 0; i < 6; i++) { +@@ -2222,7 +2298,22 @@ + } else { + OF_property_new(OF_env, dev, "assigned-addresses", NULL, 0); + } +-#if 0 ++ if (irq_line >= 0) { ++ int i; ++ OF_prop_int_new(OF_env, dev, "interrupts", 1); ++ i = pci_host_interrupt_map_len; ++ pci_host_interrupt_map[i++] = (devfn << 8) & 0xf800; ++ pci_host_interrupt_map[i++] = 0; ++ pci_host_interrupt_map[i++] = 0; ++ pci_host_interrupt_map[i++] = 0; ++ pci_host_interrupt_map[i++] = 0; /* pic handle will be patched later */ ++ pci_host_interrupt_map[i++] = irq_line; ++ if (arch != ARCH_HEATHROW) { ++ pci_host_interrupt_map[i++] = 1; ++ } ++ pci_host_interrupt_map_len = i; ++ } ++#if 1 + { + OF_prop_t *prop_name = ((OF_node_t *)dev)->prop_name; + +@@ -2390,6 +2481,54 @@ + return 0; + } + ++static void keylargo_ata(OF_node_t *mio, uint32_t base_address, ++ uint32_t base, int irq1, int irq2, ++ uint16_t pic_phandle) ++{ ++ OF_env_t *OF_env = OF_env_main; ++ OF_node_t *ata; ++ OF_regprop_t regs[2]; ++ ++ ata = OF_node_new(OF_env, mio, "ata-4", base); ++ if (ata == NULL) { ++ ERROR("Cannot create 'ata-4'\n"); ++ return; ++ } ++ OF_prop_string_new(OF_env, ata, "device_type", "ata"); ++#if 1 ++ OF_prop_string_new(OF_env, ata, "compatible", "key2largo-ata"); ++ OF_prop_string_new(OF_env, ata, "model", "ata-4"); ++ OF_prop_string_new(OF_env, ata, "cable-type", "80-conductor"); ++#else ++ OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata"); ++ OF_prop_string_new(OF_env, ata, "model", "ata-4"); ++#endif ++ OF_prop_int_new(OF_env, ata, "#address-cells", 1); ++ OF_prop_int_new(OF_env, ata, "#size-cells", 0); ++ regs[0].address = base; ++ regs[0].size = 0x00001000; ++#if 0 // HACK: Don't set up DMA registers ++ regs[1].address = 0x00008A00; ++ regs[1].size = 0x00001000; ++ OF_property_new(OF_env, ata, "reg", ++ regs, 2 * sizeof(OF_regprop_t)); ++#else ++ OF_property_new(OF_env, ata, "reg", ++ regs, sizeof(OF_regprop_t)); ++#endif ++ OF_prop_int_new(OF_env, ata, "interrupt-parent", pic_phandle); ++ regs[0].address = irq1; ++ regs[0].size = 0x00000001; ++ regs[1].address = irq2; ++ regs[1].size = 0x00000000; ++ OF_property_new(OF_env, ata, "interrupts", ++ regs, 2 * sizeof(OF_regprop_t)); ++ if (base == 0x1f000) ++ ide_pci_pmac_register(base_address + base, 0x00000000, ata); ++ else ++ ide_pci_pmac_register(0x00000000, base_address + base, ata); ++} ++ + void OF_finalize_pci_macio (void *dev, uint32_t base_address, uint32_t size, + void *private_data) + { +@@ -2398,6 +2537,8 @@ + pci_reg_prop_t pregs[2]; + OF_node_t *mio, *chs, *als; + uint16_t pic_phandle; ++ int rec_len; ++ OF_prop_t *mio_reg; + + OF_DPRINTF("mac-io: %p\n", dev); + OF_env = OF_env_main; +@@ -2416,10 +2557,14 @@ + mio = dev; + mio->private_data = private_data; + pregs[0].addr.hi = 0x00000000; +- pregs[0].addr.mid = 0x82013810; ++ pregs[0].addr.mid = 0x00000000; + pregs[0].addr.lo = 0x00000000; + pregs[0].size_hi = base_address; + pregs[0].size_lo = size; ++ mio_reg = OF_property_get(OF_env, mio, "reg"); ++ if (mio_reg && mio_reg->vlen >= 5 * 4) { ++ pregs[0].addr.mid = ((pci_reg_prop_t *)mio_reg->value)->addr.hi; ++ } + OF_property_new(OF_env, mio, "ranges", + &pregs, sizeof(pci_reg_prop_t)); + #if 0 +@@ -2431,8 +2576,32 @@ + OF_property_new(OF_env, mio, "assigned-addresses", + &pregs, sizeof(pci_reg_prop_t)); + #endif ++ ++ if (arch == ARCH_HEATHROW) { ++ /* Heathrow PIC */ ++ OF_regprop_t regs; ++ OF_node_t *mpic; ++ const char compat_str[] = "heathrow\0mac-risc"; ++ ++ mpic = OF_node_new(OF_env, mio, "interrupt-controller", 0x10); ++ if (mpic == NULL) { ++ ERROR("Cannot create 'mpic'\n"); ++ goto out; ++ } ++ OF_prop_string_new(OF_env, mpic, "device_type", "interrupt-controller"); ++ OF_property_new(OF_env, mpic, "compatible", compat_str, sizeof(compat_str)); ++ OF_prop_int_new(OF_env, mpic, "#interrupt-cells", 1); ++ regs.address = 0x10; ++ regs.size = 0x20; ++ OF_property_new(OF_env, mpic, "reg", ++ ®s, sizeof(regs)); ++ OF_property_new(OF_env, mpic, "interrupt-controller", NULL, 0); ++ pic_phandle = OF_pack_handle(OF_env, mpic); ++ OF_prop_int_new(OF_env, chs, "interrupt-controller", pic_phandle); ++ OF_node_put(OF_env, mpic); ++ rec_len = 6; ++ } else { + /* OpenPIC */ +- { + OF_regprop_t regs[4]; + OF_node_t *mpic; + mpic = OF_node_new(OF_env, mio, "interrupt-controller", 0x40000); +@@ -2455,8 +2624,37 @@ + pic_phandle = OF_pack_handle(OF_env, mpic); + OF_prop_int_new(OF_env, chs, "interrupt-controller", pic_phandle); + OF_node_put(OF_env, mpic); ++ rec_len = 7; + } +-#if 1 ++ ++ /* patch pci host table */ ++ /* XXX: do it after the PCI init */ ++ { ++ int i; ++ uint32_t tab[4]; ++ ++ for(i = 0; i < pci_host_interrupt_map_len; i += rec_len) ++ pci_host_interrupt_map[i + 4] = pic_phandle; ++#if 0 ++ dprintf("interrupt-map:\n"); ++ for(i = 0; i < pci_host_interrupt_map_len; i++) { ++ dprintf(" %08x", pci_host_interrupt_map[i]); ++ if ((i % rec_len) == (rec_len - 1)) ++ dprintf("\n"); ++ } ++ dprintf("\n"); ++#endif ++ OF_property_new(OF_env, pci_host_node, "interrupt-map", ++ pci_host_interrupt_map, ++ pci_host_interrupt_map_len * sizeof(uint32_t)); ++ tab[0] = 0xf800; ++ tab[1] = 0; ++ tab[2] = 0; ++ tab[3] = 0; ++ OF_property_new(OF_env, pci_host_node, "interrupt-map-mask", ++ tab, 4 * sizeof(uint32_t)); ++ } ++#if 0 + /* escc is usefull to get MacOS X debug messages */ + { + OF_regprop_t regs[8]; +@@ -2645,85 +2843,12 @@ + OF_node_put(OF_env, scc); + } + #endif +- /* IDE controller */ +- { +- OF_node_t *ata; +- OF_regprop_t regs[2]; +- ata = OF_node_new(OF_env, mio, "ata-4", 0x1f000); +- if (ata == NULL) { +- ERROR("Cannot create 'ata-4'\n"); +- goto out; +- } +- OF_prop_string_new(OF_env, ata, "device_type", "ata"); +-#if 1 +- OF_prop_string_new(OF_env, ata, "compatible", "keylargo-ata"); +- OF_prop_string_new(OF_env, ata, "model", "ata-4"); +-#else +- OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata"); +- OF_prop_string_new(OF_env, ata, "model", "ata-4"); +-#endif +- OF_prop_int_new(OF_env, ata, "#address-cells", 1); +- OF_prop_int_new(OF_env, ata, "#size-cells", 0); +- regs[0].address = 0x0001F000; +- regs[0].size = 0x00001000; +-#if 0 // HACK: Don't set up DMA registers +- regs[1].address = 0x00008A00; +- regs[1].size = 0x00001000; +- OF_property_new(OF_env, ata, "reg", +- regs, 2 * sizeof(OF_regprop_t)); +-#else +- OF_property_new(OF_env, ata, "reg", +- regs, sizeof(OF_regprop_t)); +-#endif +- OF_prop_int_new(OF_env, ata, "interrupt-parent", pic_phandle); +- regs[0].address = 0x00000013; +- regs[0].size = 0x00000001; +- regs[1].address = 0x0000000B; +- regs[1].size = 0x00000000; +- OF_property_new(OF_env, ata, "interrupts", +- regs, 2 * sizeof(OF_regprop_t)); +- ide_pci_pmac_register(base_address + 0x1f000, 0x00000000, ata); +- +- } +- { +- OF_node_t *ata; +- OF_regprop_t regs[2]; +- ata = OF_node_new(OF_env, mio, "ata-4", 0x20000); +- if (ata == NULL) { +- ERROR("Cannot create 'ata-4'\n"); +- goto out; +- } +- OF_prop_string_new(OF_env, ata, "device_type", "ata"); +-#if 1 +- OF_prop_string_new(OF_env, ata, "compatible", "keylargo-ata"); +- OF_prop_string_new(OF_env, ata, "model", "ata-4"); +-#else +- OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata"); +- OF_prop_string_new(OF_env, ata, "model", "ata-4"); +-#endif +- OF_prop_int_new(OF_env, ata, "#address-cells", 1); +- OF_prop_int_new(OF_env, ata, "#size-cells", 0); +- regs[0].address = 0x00020000; +- regs[0].size = 0x00001000; +-#if 0 // HACK: Don't set up DMA registers +- regs[1].address = 0x00008A00; +- regs[1].size = 0x00001000; +- OF_property_new(OF_env, ata, "reg", +- regs, 2 * sizeof(OF_regprop_t)); +-#else +- OF_property_new(OF_env, ata, "reg", +- regs, sizeof(OF_regprop_t)); +-#endif +- OF_prop_int_new(OF_env, ata, "interrupt-parent", pic_phandle); +- regs[0].address = 0x00000014; +- regs[0].size = 0x00000001; +- regs[1].address = 0x0000000B; +- regs[1].size = 0x00000000; +- OF_property_new(OF_env, ata, "interrupts", +- regs, 2 * sizeof(OF_regprop_t)); +- ide_pci_pmac_register(0x00000000, base_address + 0x20000, ata); +- ++ /* Keylargo IDE controller: need some work (DMA problem ?) */ ++ if (arch == ARCH_MAC99) { ++ keylargo_ata(mio, base_address, 0x1f000, 0x13, 0xb, pic_phandle); ++ keylargo_ata(mio, base_address, 0x20000, 0x14, 0xb, pic_phandle); + } ++#if 0 + /* Timer */ + { + OF_node_t *tmr; +@@ -2746,10 +2871,11 @@ + regs, sizeof(OF_regprop_t)); + OF_node_put(OF_env, tmr); + } ++#endif + /* VIA-PMU */ + { + /* Controls adb, RTC and power-mgt (forget it !) */ +- OF_node_t *via, *adb, *rtc; ++ OF_node_t *via, *adb; + OF_regprop_t regs[1]; + #if 0 // THIS IS A HACK AND IS COMPLETELY ABSURD ! + // (but needed has Qemu doesn't emulate via-pmu). +@@ -2773,14 +2899,21 @@ + regs[0].size = 0x00002000; + OF_property_new(OF_env, via, "reg", regs, sizeof(OF_regprop_t)); + OF_prop_int_new(OF_env, via, "interrupt-parent", pic_phandle); ++ if (arch == ARCH_HEATHROW) { ++ OF_prop_int_new(OF_env, via, "interrupts", 0x12); ++ } else { + regs[0].address = 0x00000019; + regs[0].size = 0x00000001; + OF_property_new(OF_env, via, "interrupts", + regs, sizeof(OF_regprop_t)); ++ } ++ /* force usage of OF bus speeds */ ++ OF_prop_int_new(OF_env, via, "BusSpeedCorrect", 1); + #if 0 + OF_prop_int_new(OF_env, via, "pmu-version", 0x00D0740C); + #endif +-#if 1 ++ { ++ OF_node_t *kbd, *mouse; + /* ADB pseudo-device */ + adb = OF_node_new(OF_env, via, "adb", OF_ADDRESS_NONE); + if (adb == NULL) { +@@ -2797,9 +2930,26 @@ + OF_prop_int_new(OF_env, adb, "#size-cells", 0); + OF_pack_get_path(OF_env, tmp, 512, adb); + OF_prop_string_new(OF_env, als, "adb", tmp); +- /* XXX: add "keyboard@2" and "mouse@3" */ +- OF_node_put(OF_env, adb); +-#endif ++ ++ kbd = OF_node_new(OF_env, adb, "keyboard", 2); ++ if (kbd == NULL) { ++ ERROR("Cannot create 'kbd'\n"); ++ goto out; ++ } ++ OF_prop_string_new(OF_env, kbd, "device_type", "keyboard"); ++ OF_prop_int_new(OF_env, kbd, "reg", 2); ++ ++ mouse = OF_node_new(OF_env, adb, "mouse", 3); ++ if (mouse == NULL) { ++ ERROR("Cannot create 'mouse'\n"); ++ goto out; ++ } ++ OF_prop_string_new(OF_env, mouse, "device_type", "mouse"); ++ OF_prop_int_new(OF_env, mouse, "reg", 3); ++ OF_prop_int_new(OF_env, mouse, "#buttons", 3); ++ } ++ { ++ OF_node_t *rtc; + + rtc = OF_node_new(OF_env, via, "rtc", OF_ADDRESS_NONE); + if (rtc == NULL) { +@@ -2813,14 +2963,68 @@ + OF_prop_string_new(OF_env, rtc, "compatible", "rtc"); + #endif + OF_node_put(OF_env, rtc); +- OF_node_put(OF_env, via); + } ++ // OF_node_put(OF_env, via); ++ } ++ { ++ OF_node_t *pmgt; ++ pmgt = OF_node_new(OF_env, mio, "power-mgt", OF_ADDRESS_NONE); ++ OF_prop_string_new(OF_env, pmgt, "device_type", "power-mgt"); ++ OF_prop_string_new(OF_env, pmgt, "compatible", "cuda"); ++ OF_prop_string_new(OF_env, pmgt, "mgt-kind", "min-consumption-pwm-led"); ++ OF_node_put(OF_env, pmgt); ++ } ++ ++ if (arch == ARCH_HEATHROW) { ++ /* NVRAM */ ++ OF_node_t *nvr; ++ OF_regprop_t regs; ++ nvr = OF_node_new(OF_env, mio, "nvram", 0x60000); ++ OF_prop_string_new(OF_env, nvr, "device_type", "nvram"); ++ regs.address = 0x60000; ++ regs.size = 0x00020000; ++ OF_property_new(OF_env, nvr, "reg", ®s, sizeof(regs)); ++ OF_prop_int_new(OF_env, nvr, "#bytes", 0x2000); ++ OF_node_put(OF_env, nvr); ++ } ++ + out: + // OF_node_put(OF_env, mio); + OF_node_put(OF_env, chs); + OF_node_put(OF_env, als); + } + ++void OF_finalize_pci_ide (void *dev, ++ uint32_t io_base0, uint32_t io_base1, ++ uint32_t io_base2, uint32_t io_base3) ++{ ++ OF_env_t *OF_env = OF_env_main; ++ OF_node_t *pci_ata = dev; ++ OF_node_t *ata, *atas[2]; ++ int i; ++ ++ OF_prop_int_new(OF_env, pci_ata, "#address-cells", 1); ++ OF_prop_int_new(OF_env, pci_ata, "#size-cells", 0); ++ ++ /* XXX: Darwin handles only one device */ ++ for(i = 0; i < 1; i++) { ++ ata = OF_node_new(OF_env, pci_ata, "ata-4", i); ++ if (ata == NULL) { ++ ERROR("Cannot create 'ata-4'\n"); ++ return; ++ } ++ OF_prop_string_new(OF_env, ata, "device_type", "ata"); ++ OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata"); ++ OF_prop_string_new(OF_env, ata, "model", "ata-4"); ++ OF_prop_int_new(OF_env, ata, "#address-cells", 1); ++ OF_prop_int_new(OF_env, ata, "#size-cells", 0); ++ OF_prop_int_new(OF_env, ata, "reg", i); ++ atas[i] = ata; ++ } ++ ide_pci_pc_register(io_base0, io_base1, io_base2, io_base3, ++ atas[0], atas[1]); ++} ++ + /*****************************************************************************/ + /* Fake package */ + static void OF_method_fake (OF_env_t *OF_env) +@@ -2862,11 +3066,11 @@ + /* As we get a 1:1 mapping, do nothing */ + ihandle = popd(OF_env); + args = (void *)popd(OF_env); +- address = popd(OF_env); +- virt = popd(OF_env); +- size = popd(OF_env); + popd(OF_env); +- OF_DPRINTF("Translate address %0x %0x %0x %0x\n", ihandle, address, ++ size = popd(OF_env); ++ virt = popd(OF_env); ++ address = popd(OF_env); ++ OF_DPRINTF("Map %0x %0x %0x %0x\n", ihandle, address, + virt, size); + pushd(OF_env, 0); + } +@@ -3270,7 +3474,7 @@ + OF_prop_string_new(OF_env, dsk, "device_type", "block"); + OF_prop_string_new(OF_env, dsk, "category", type); + OF_prop_int_new(OF_env, dsk, "device_id", devnum); +- OF_prop_int_new(OF_env, dsk, "reg", 0); ++ OF_prop_int_new(OF_env, dsk, "reg", devnum); + OF_method_new(OF_env, dsk, "open", &OF_blockdev_open); + OF_method_new(OF_env, dsk, "seek", &OF_blockdev_seek); + OF_method_new(OF_env, dsk, "read", &OF_blockdev_read); +@@ -3432,7 +3636,8 @@ + } + + void OF_vga_register (const unsigned char *name, unused uint32_t address, +- int width, int height, int depth) ++ int width, int height, int depth, ++ unsigned long vga_bios_addr, unsigned long vga_bios_size) + { + OF_env_t *OF_env; + unsigned char tmp[OF_NAMELEN_MAX]; +@@ -3504,6 +3709,18 @@ + OF_prop_string_new(OF_env, als, "display", tmp); + OF_node_put(OF_env, als); + /* XXX: may also need read-rectangle */ ++ ++ if (vga_bios_size >= 8) { ++ const uint8_t *p; ++ int size; ++ /* check the QEMU VGA BIOS header */ ++ p = (const uint8_t *)vga_bios_addr; ++ if (p[0] == 'N' && p[1] == 'D' && p[2] == 'R' && p[3] == 'V') { ++ size = *(uint32_t *)(p + 4); ++ OF_property_new(OF_env, disp, "driver,AAPL,MacOS,PowerPC", ++ p + 8, size); ++ } ++ } + out: + OF_node_put(OF_env, disp); + } +@@ -4451,7 +4668,10 @@ + break; + case 0x233441d3: /* MacOS X 10.2 and OpenDarwin 1.41 */ + /* Create "memory-map" pseudo device */ +- popd(OF_env); ++ { ++ OF_node_t *map; ++ uint32_t phandle; ++ + /* Find "/packages" */ + chs = OF_pack_find_by_name(OF_env, OF_node_root, "/chosen"); + if (chs == NULL) { +@@ -4459,10 +4679,6 @@ + ERROR("Cannot get '/chosen'\n"); + break; + } +- { +-#if 1 +- OF_node_t *map; +- uint32_t phandle; + map = OF_node_new(OF_env, chs, "memory-map", OF_ADDRESS_NONE); + if (map == NULL) { + pushd(OF_env, -1); +@@ -4473,11 +4689,8 @@ + OF_node_put(OF_env, map); + OF_node_put(OF_env, chs); + pushd(OF_env, phandle); +- } +-#else +- pushd(OF_env, 0); +-#endif + pushd(OF_env, 0); ++ } + break; + case 0x32a2d18e: /* MacOS X 10.2 and OpenDarwin 6.02 */ + /* Return screen ihandle */ +@@ -4540,9 +4753,10 @@ + case 0x4ad41f2d: + /* Yaboot: wait 10 ms: sure ! */ + break; ++ + default: + /* ERROR */ +- printf("Script:\n%s\n", FString); ++ printf("Script: len=%d\n%s\n", (int)strlen(FString), FString); + printf("Call %0x NOT IMPLEMENTED !\n", crc); + bug(); + break; +@@ -4581,6 +4795,7 @@ + { + OF_CHECK_NBARGS(OF_env, 0); + /* Should free all OF resources */ ++ bd_reset_all(); + #if defined (DEBUG_BIOS) + { + uint16_t loglevel = 0x02 | 0x10 | 0x80; +diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/pci.c OpenHackWare-release-0.4/src/pci.c +--- OpenHackWare-release-0.4.org/src/pci.c 2005-03-31 09:23:33.000000000 +0200 ++++ OpenHackWare-release-0.4/src/pci.c 2005-07-07 23:27:37.000000000 +0200 +@@ -99,8 +99,8 @@ + uint16_t min_grant; + uint16_t max_latency; + uint8_t irq_line; +- uint32_t regions[6]; +- uint32_t sizes[6]; ++ uint32_t regions[7]; /* the region 6 is the PCI ROM */ ++ uint32_t sizes[7]; + pci_device_t *next; + }; + +@@ -158,6 +158,7 @@ + + /* IRQ numbers assigned to PCI IRQs */ + static uint8_t prep_pci_irqs[4] = { 9, 11, 9, 11 }; ++static uint8_t heathrow_pci_irqs[4] = { 0x15, 0x16, 0x17, 0x18 }; + static uint8_t pmac_pci_irqs[4] = { 8, 9, 10, 11 }; + + /* PREP PCI host */ +@@ -399,6 +400,79 @@ + &uninorth_config_readl, &uninorth_config_writel, + }; + ++/* Grackle PCI host */ ++ ++static uint32_t grackle_cfg_address (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset) ++{ ++ uint32_t addr; ++ addr = 0x80000000 | (bus << 16) | (devfn << 8) | (offset & 0xfc); ++ stswap32((uint32_t *)bridge->cfg_addr, addr); ++ return bridge->cfg_data + (offset & 3); ++} ++ ++static uint8_t grackle_config_readb (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset) ++{ ++ uint32_t addr; ++ addr = grackle_cfg_address(bridge, bus, devfn, offset); ++ return *((uint8_t *)addr); ++} ++ ++static void grackle_config_writeb (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset, uint8_t val) ++{ ++ uint32_t addr; ++ addr = grackle_cfg_address(bridge, bus, devfn, offset); ++ *((uint8_t *)addr) = val; ++} ++ ++static uint16_t grackle_config_readw (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset) ++{ ++ uint32_t addr; ++ addr = grackle_cfg_address(bridge, bus, devfn, offset); ++ return ldswap16((uint16_t *)addr); ++} ++ ++static void grackle_config_writew (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset, uint16_t val) ++{ ++ uint32_t addr; ++ addr = grackle_cfg_address(bridge, bus, devfn, offset); ++ stswap16((uint16_t *)addr, val); ++} ++ ++static uint32_t grackle_config_readl (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset) ++{ ++ uint32_t addr; ++ addr = grackle_cfg_address(bridge, bus, devfn, offset); ++ return ldswap32((uint32_t *)addr); ++} ++ ++static void grackle_config_writel (pci_bridge_t *bridge, ++ uint8_t bus, uint8_t devfn, ++ uint8_t offset, uint32_t val) ++{ ++ uint32_t addr; ++ ++ addr = grackle_cfg_address(bridge, bus, devfn, offset); ++ stswap32((uint32_t *)addr, val); ++} ++ ++static pci_ops_t grackle_pci_ops = { ++ &grackle_config_readb, &grackle_config_writeb, ++ &grackle_config_readw, &grackle_config_writew, ++ &grackle_config_readl, &grackle_config_writel, ++}; ++ + static inline uint8_t pci_config_readb (pci_bridge_t *bridge, + uint8_t bus, uint8_t devfn, + uint8_t offset) +@@ -466,12 +540,22 @@ + }, + }; + ++static int ide_config_cb2 (pci_device_t *device) ++{ ++ OF_finalize_pci_ide(device->common.OF_private, ++ device->regions[0] & ~0x0000000F, ++ device->regions[1] & ~0x0000000F, ++ device->regions[2] & ~0x0000000F, ++ device->regions[3] & ~0x0000000F); ++ return 0; ++} ++ + static pci_dev_t ide_devices[] = { + { +- 0x8086, 0x0100, +- NULL, "Qemu IDE", "Qemu IDE", "ide", ++ 0x1095, 0x0646, /* CMD646 IDE controller */ ++ "pci-ide", "pci-ata", NULL, NULL, + 0, 0, 0, +- NULL, NULL, ++ ide_config_cb2, NULL, + }, + { + 0xFFFF, 0xFFFF, +@@ -481,7 +565,9 @@ + }, + }; + +-static int ide_config_cb (pci_device_t *device) ++#if 0 ++/* should base it on PCI ID, not on arch */ ++static int ide_config_cb (unused pci_device_t *device) + { + printf("Register IDE controller\n"); + switch (arch) { +@@ -491,14 +577,8 @@ + device->common.OF_private); + break; + default: +- ide_pci_pc_register(device->regions[0] & ~0x0000000F, +- device->regions[1] & ~0x0000000F, +- device->regions[2] & ~0x0000000F, +- device->regions[3] & ~0x0000000F, +- device->common.OF_private); + break; + } +- + return 0; + } + +@@ -512,16 +592,12 @@ + device->common.OF_private); + break; + default: +- ide_pci_pc_register(device->regions[0] & ~0x0000000F, +- device->regions[1] & ~0x0000000F, +- device->regions[2] & ~0x0000000F, +- device->regions[3] & ~0x0000000F, +- device->common.OF_private); + break; + } + + return 0; + } ++#endif + + static pci_subclass_t mass_subclass[] = { + { +@@ -530,7 +606,7 @@ + }, + { + 0x01, "IDE controller", "ide", ide_devices, NULL, +- &ide_config_cb, NULL, ++ NULL, NULL, + }, + { + 0x02, "Floppy disk controller", NULL, NULL, NULL, +@@ -546,7 +622,7 @@ + }, + { + 0x05, "ATA controller", "ata", NULL, NULL, +- &ata_config_cb, NULL, ++ NULL, NULL, + }, + { + 0x80, "misc mass-storage controller", NULL, NULL, NULL, +@@ -646,7 +722,9 @@ + /* VGA 640x480x16 */ + OF_vga_register(device->common.device->name, + device->regions[0] & ~0x0000000F, +- vga_width, vga_height, vga_depth); ++ vga_width, vga_height, vga_depth, ++ device->regions[6] & ~0x0000000F, ++ device->sizes[6]); + } + vga_console_register(); + +@@ -750,6 +828,13 @@ + NULL, &PREP_pci_ops, + }; + ++pci_dev_t grackle_fake_bridge = { ++ 0xFFFF, 0xFFFF, ++ "pci", "pci-bridge", "DEC,21154", "DEC,21154.pci-bridge", ++ -1, -1, -1, ++ NULL, &grackle_pci_ops, ++}; ++ + static pci_dev_t hbrg_devices[] = { + { + 0x106B, 0x0020, NULL, +@@ -758,8 +843,8 @@ + NULL, &uninorth_agp_fake_bridge, + }, + { +- 0x106B, 0x001F, +- NULL, "pci", "AAPL,UniNorth", "uni-north", ++ 0x106B, 0x001F, NULL, ++ "pci", "AAPL,UniNorth", "uni-north", + 3, 2, 1, + NULL, &uninorth_fake_bridge, + }, +@@ -770,10 +855,10 @@ + NULL, &uninorth_fake_bridge, + }, + { +- 0x1011, 0x0026, NULL, +- "pci-bridge", NULL, NULL, ++ 0x1057, 0x0002, "pci", ++ "pci", "MOT,MPC106", "grackle", + 3, 2, 1, +- NULL, &PREP_pci_ops, ++ NULL, &grackle_fake_bridge, + }, + { + 0x1057, 0x4801, NULL, +@@ -1443,7 +1528,14 @@ + } + + static const pci_dev_t misc_pci[] = { +- /* Apple Mac-io controller */ ++ /* Paddington Mac I/O */ ++ { ++ 0x106B, 0x0017, ++ "mac-io", "mac-io", "AAPL,343S1211", "paddington\1heathrow", ++ 1, 1, 1, ++ &macio_config_cb, NULL, ++ }, ++ /* KeyLargo Mac I/O */ + { + 0x106B, 0x0022, + "mac-io", "mac-io", "AAPL,Keylargo", "Keylargo", +@@ -1599,7 +1691,7 @@ + uint8_t min_grant, uint8_t max_latency, + int irq_line) + { +- uint32_t cmd; ++ uint32_t cmd, addr; + int i; + + device->min_grant = min_grant; +@@ -1611,22 +1703,28 @@ + printf("MAP PCI device %d:%d to IRQ %d\n", + device->bus, device->devfn, irq_line); + } +- for (i = 0; i < 6; i++) { ++ for (i = 0; i < 7; i++) { + if ((device->regions[i] & ~0xF) != 0x00000000 && + (device->regions[i] & ~0xF) != 0xFFFFFFF0) { + printf("Map PCI device %d:%d %d to %0x %0x (%s)\n", + device->bus, device->devfn, i, + device->regions[i], device->sizes[i], +- device->regions[i] & 0x00000001 ? "I/O" : "memory"); ++ (device->regions[i] & 0x00000001) && i != 6 ? "I/O" : ++ "memory"); ++ if (i != 6) { + cmd = pci_config_readl(bridge, device->bus, device->devfn, 0x04); + if (device->regions[i] & 0x00000001) + cmd |= 0x00000001; + else + cmd |= 0x00000002; + pci_config_writel(bridge, device->bus, device->devfn, 0x04, cmd); ++ } ++ if (i == 6) ++ addr = 0x30; /* PCI ROM */ ++ else ++ addr = 0x10 + (i * sizeof(uint32_t)); + pci_config_writel(bridge, device->bus, device->devfn, +- 0x10 + (i * sizeof(uint32_t)), +- device->regions[i]); ++ addr, device->regions[i]); + } + } + } +@@ -1900,7 +1998,7 @@ + goto out; + } + ret = (pci_u_t *)newd; +- max_areas = 6; ++ max_areas = 7; + /* register PCI device in OF tree */ + if (bridge->dev.common.type == PCI_FAKE_BRIDGE) { + newd->common.OF_private = +@@ -1927,6 +2025,9 @@ + /* Handle 64 bits memory mapping */ + continue; + } ++ if (i == 6) ++ addr = 0x30; /* PCI ROM */ ++ else + addr = 0x10 + (i * sizeof(uint32_t)); + /* Get region size + * Note: we assume it's always a power of 2 +@@ -1935,7 +2036,7 @@ + smask = pci_config_readl(bridge, bus, devfn, addr); + if (smask == 0x00000000 || smask == 0xFFFFFFFF) + continue; +- if (smask & 0x00000001) { ++ if ((smask & 0x00000001) != 0 && i != 6) { + /* I/O space */ + base = io_base; + /* Align to a minimum of 256 bytes (arbitrary) */ +@@ -1947,6 +2048,8 @@ + /* Align to a minimum of 64 kB (arbitrary) */ + min_align = 1 << 16; + amask = 0x0000000F; ++ if (i == 6) ++ smask |= 1; /* PCI ROM enable */ + } + omask = smask & amask; + smask &= ~amask; +@@ -1980,7 +2083,10 @@ + if (irq_pin > 0) { + /* assign the IRQ */ + irq_pin = ((devfn >> 3) + irq_pin - 1) & 3; +- if (arch == ARCH_PREP) { ++ /* XXX: should base it on the PCI bridge type, not the arch */ ++ switch(arch) { ++ case ARCH_PREP: ++ { + int elcr_port, val; + irq_line = prep_pci_irqs[irq_pin]; + /* set the IRQ to level-sensitive */ +@@ -1988,14 +2094,22 @@ + val = inb(elcr_port); + val |= 1 << (irq_line & 7); + outb(elcr_port, val); +- } else { ++ } ++ break; ++ case ARCH_MAC99: + irq_line = pmac_pci_irqs[irq_pin]; ++ break; ++ case ARCH_HEATHROW: ++ irq_line = heathrow_pci_irqs[irq_pin]; ++ break; ++ default: ++ break; + } + } + update_device: + pci_update_device(bridge, newd, min_grant, max_latency, irq_line); + OF_finalize_pci_device(newd->common.OF_private, bus, devfn, +- newd->regions, newd->sizes); ++ newd->regions, newd->sizes, irq_line); + /* Call special inits if needed */ + if (dev->config_cb != NULL) + (*dev->config_cb)(newd); +@@ -2049,6 +2163,32 @@ + case ARCH_CHRP: + /* TODO */ + break; ++ case ARCH_HEATHROW: ++ dev = pci_find_device(0x06, 0x00, 0xFF, checkv, checkp); ++ if (dev == NULL) ++ return -1; ++ fake_host = pci_add_host(hostp, dev, ++ (0x06 << 24) | (0x00 << 16) | (0xFF << 8)); ++ if (fake_host == NULL) ++ return -1; ++ fake_host->dev.common.type = PCI_FAKE_HOST; ++ dev = &grackle_fake_bridge; ++ if (dev == NULL) ++ goto free_fake_host; ++ fake_bridge = pci_add_bridge(fake_host, 0, 0, dev, ++ (0x06 << 24) | (0x04 << 16) | (0xFF << 8), ++ cfg_base, cfg_len, ++ cfg_base + 0x7ec00000, ++ cfg_base + 0x7ee00000, ++ mem_base, mem_len, ++ io_base, io_len, ++ rbase, rlen, ++ 0, ++ &grackle_pci_ops); ++ if (fake_bridge == NULL) ++ goto free_fake_host; ++ fake_bridge->dev.common.type = PCI_FAKE_BRIDGE; ++ break; + case ARCH_MAC99: + dev = pci_find_device(0x06, 0x00, 0xFF, checkv, checkp); + if (dev == NULL) +@@ -2167,6 +2307,30 @@ + case ARCH_CHRP: + /* TODO */ + break; ++ case ARCH_HEATHROW: ++ cfg_base = 0x80000000; ++ cfg_len = 0x7f000000; ++ mem_base = 0x80000000; ++ mem_len = 0x01000000; ++ io_base = 0xfe000000; ++ io_len = 0x00800000; ++#if 1 ++ rbase = 0xfd000000; ++ rlen = 0x01000000; ++#else ++ rbase = 0x00000000; ++ rlen = 0x01000000; ++#endif ++ if (pci_check_host(&pci_main, cfg_base, cfg_len, ++ mem_base, mem_len, io_base, io_len, rbase, rlen, ++ 0x1057, 0x0002) == 0) { ++ isa_io_base = io_base; ++ busnum++; ++ } ++ for (curh = pci_main; curh->next != NULL; curh = curh->next) ++ continue; ++ pci_check_devices(curh); ++ break; + case ARCH_MAC99: + /* We are supposed to have 3 host bridges: + * - the uninorth AGP bridge at 0xF0000000 diff --git a/tools/ioemu/pc-bios/proll.patch b/tools/ioemu/pc-bios/proll.patch new file mode 100644 index 0000000000..cc69519efb --- /dev/null +++ b/tools/ioemu/pc-bios/proll.patch @@ -0,0 +1,4067 @@ +diff -ruN proll_18.orig/Makefile proll-patch-15/Makefile +--- proll_18.orig/Makefile 2002-09-13 14:16:59.000000000 +0000 ++++ proll-patch-15/Makefile 2005-11-09 18:14:51.000000000 +0000 +@@ -4,6 +4,7 @@ + make -C krups-ser all + make -C espresso all + make -C espresso-ser all ++ make -C qemu all + + clean: + make -C mrcoffee clean +@@ -11,3 +12,4 @@ + make -C krups-ser clean + make -C espresso clean + make -C espresso-ser clean ++ make -C qemu clean +diff -ruN proll_18.orig/qemu/Makefile proll-patch-15/qemu/Makefile +--- proll_18.orig/qemu/Makefile 1970-01-01 00:00:00.000000000 +0000 ++++ proll-patch-15/qemu/Makefile 2005-08-14 10:25:06.000000000 +0000 +@@ -0,0 +1,123 @@ ++# ++# proll: ++# qemu/Makefile - make PROLL for QEMU ++# $Id: proll.patch,v 1.6 2005/11/11 00:24:57 bellard Exp $ ++# ++# Copyright 1999 Pete Zaitcev ++# This is Free Software is licensed under terms of GNU General Public License. ++# ++ ++CC = gcc ++ ++#CROSS = /usr/local/sparc/bin/sparc-sun-linux- ++CROSS = sparc-unknown-linux-gnu- ++ ++CROSSCC = $(CROSS)gcc ++CROSSLD = $(CROSS)ld ++CROSSNM = $(CROSS)nm ++ ++RM = /bin/rm -f ++ELFTOAOUT = elftoaout ++ ++# ++SRC = ../src ++ ++# Due to remapping algorithm PROLBASE should be algned on PMD. ++# We make PROLBASE a define instead of using _start because we ++# want to shift it to form a PGD entry. A relocatable label will not work. ++# Linux kernel expects us to be at LINUX_OPPROM_BEGVM . ++PROLBASE = 0xffd00000 ++PROLRODATA = 0xffd08000 ++PROLDATA = 0xffd0b000 ++PROLSIZE = 240*1024 ++ ++# Linux ++# Fixed %g6 is for arch/sparc/kernel/head.S, it seems ok w/o -ffixed-g6. ++# Kernel uses -fcall-used-g5 -fcall-used-g7, we probably do not need them. ++# __ANSI__ is supposed to be on by default but it is not. ++CFLAGS = -O2 -W -Wall -DPROLBASE=$(PROLBASE) -DPROLDATA=$(PROLDATA) -DPROLRODATA=$(PROLRODATA) -D__ANSI__=1 -I$(SRC) -mcpu=hypersparc -Wa,-xarch=v8 -g -DQEMU -m32 -fno-builtin ++ASFLAGS = -D__ASSEMBLY__ -I$(SRC) -DPROLRODATA=$(PROLRODATA) -DPROLDATA=$(PROLDATA) -DPROLSIZE=$(PROLSIZE) -g -Wa,-xarch=v8 -Wa,-32 ++# Solaris or Linux/i386 cross compilation ++#CFLAGS = -Iinclude -O ++ ++LDFLAGS = -N -Ttext $(PROLBASE) --section-start .rodata=$(PROLRODATA) -Tdata $(PROLDATA) -Tbss $(PROLDATA) ++ ++ALL = proll.aout ++PROLLEXE = proll.elf ++ ++OBJS = head.o wuf.o wof.o main.o $(CONSOLE) \ ++ printf.o le.o system_qemu.o iommu.o \ ++ arp.o netinit.o bootp.o packet.o tftp.o udp.o sched_4m.o openprom.o \ ++ vconsole.o hconsole.o rconsole.o vcons_zs.o esp.o ++ ++all: $(ALL) ++ ++$(PROLLEXE): $(OBJS) ++ $(CROSSLD) $(LDFLAGS) -o $(PROLLEXE) $(OBJS) ++ ++head.o: head.S $(SRC)/phys_jj.h \ ++ $(SRC)/asi.h $(SRC)/psr.h $(SRC)/crs.h ++ $(CROSSCC) $(ASFLAGS) -DPROLBASE=$(PROLBASE) -o $*.o -c $*.S ++ ++main.o: main.c $(SRC)/asi.h $(SRC)/pgtsrmmu.h $(SRC)/iommu.h \ ++ $(SRC)/phys_jj.h $(SRC)/vconsole.h $(SRC)/version.h $(SRC)/general.h \ ++ $(SRC)/net.h $(SRC)/romlib.h $(SRC)/netpriv.h $(SRC)/arpa.h $(SRC)/system.h ++ $(CROSSCC) $(CFLAGS) -c $*.c ++openprom.o: openprom.c $(SRC)/openprom.h $(SRC)/general.h $(SRC)/romlib.h \ ++ $(SRC)/vconsole.h $(SRC)/system.h $(SRC)/phys_jj.h ++ $(CROSSCC) $(CFLAGS) -c $*.c ++ ++system_qemu.o: system_qemu.c $(SRC)/vconsole.h $(SRC)/pgtsrmmu.h \ ++ $(SRC)/timer.h $(SRC)/general.h $(SRC)/net.h $(SRC)/romlib.h $(SRC)/asi.h \ ++ $(SRC)/netpriv.h $(SRC)/arpa.h $(SRC)/system.h $(SRC)/crs.h ++ $(CROSSCC) $(CFLAGS) -c $*.c ++iommu.o: $(SRC)/iommu.c $(SRC)/pgtsrmmu.h $(SRC)/phys_jj.h $(SRC)/iommu.h \ ++ $(SRC)/vconsole.h $(SRC)/general.h $(SRC)/romlib.h $(SRC)/system.h $(SRC)/asi.h ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++vconsole.o: $(SRC)/vconsole.c $(SRC)/vconsole.h $(SRC)/hconsole.h ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++vcons_zs.o: $(SRC)/vcons_zs.c $(SRC)/vconsole.h $(SRC)/system.h ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++hconsole.o: $(SRC)/hconsole.c $(SRC)/hconsole.h $(SRC)/rconsole.h $(SRC)/phys_jj.h ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++rconsole.o: $(SRC)/rconsole.c $(SRC)/rconsole.h ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++printf.o: $(SRC)/printf.c ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++le.o: $(SRC)/le.c $(SRC)/dma.h $(SRC)/system.h $(SRC)/netpriv.h $(SRC)/romlib.h $(SRC)/general.h $(SRC)/net.h $(SRC)/phys_jj.h ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++esp.o: $(SRC)/esp.c $(SRC)/dma.h $(SRC)/system.h $(SRC)/romlib.h $(SRC)/general.h $(SRC)/phys_jj.h ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++arp.o: $(SRC)/arp.c $(SRC)/general.h $(SRC)/net.h $(SRC)/romlib.h $(SRC)/netpriv.h $(SRC)/arp.h ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++netinit.o: $(SRC)/netinit.c $(SRC)/general.h $(SRC)/net.h $(SRC)/romlib.h $(SRC)/netpriv.h $(SRC)/arp.h $(SRC)/ip.h $(SRC)/udp.h ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++tftp.o: $(SRC)/tftp.c $(SRC)/general.h $(SRC)/net.h $(SRC)/arpa.h $(SRC)/romlib.h $(SRC)/tftp.h ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++udp.o: $(SRC)/udp.c $(SRC)/general.h $(SRC)/net.h $(SRC)/romlib.h $(SRC)/netpriv.h $(SRC)/arp.h $(SRC)/ip.h $(SRC)/udp.h ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++packet.o: $(SRC)/packet.c $(SRC)/general.h $(SRC)/net.h $(SRC)/romlib.h $(SRC)/netpriv.h ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++sched_4m.o: $(SRC)/sched_4m.c $(SRC)/system.h $(SRC)/general.h $(SRC)/romlib.h $(SRC)/phys_jj.h ++ $(CROSSCC) $(CFLAGS) -c $(SRC)/$*.c ++bootp.o: $(SRC)/bootp.c $(SRC)/general.h $(SRC)/net.h \ ++ $(SRC)/arpa.h $(SRC)/romlib.h $(SRC)/system.h $(SRC)/bootp.h ++ $(CROSSCC) $(CFLAGS) -DNOBPEXT=1 -c $(SRC)/$*.c ++ ++wuf.o: $(SRC)/wuf.S ++ $(CROSSCC) $(ASFLAGS) -o $*.o -c $(SRC)/$*.S ++wof.o: $(SRC)/wof.S ++ $(CROSSCC) $(ASFLAGS) -o $*.o -c $(SRC)/$*.S ++ ++#genlab.o: genlab.c ++# $(CC) -c $*.c ++# ++#genlab: genlab.o ++# $(CC) -o genlab genlab.o ++ ++clean: ++ $(RM) $(OBJS) ++ $(RM) $(PROLLEXE) proll.aout ++ ++proll.aout: $(PROLLEXE) ++ $(ELFTOAOUT) -o proll.aout $(PROLLEXE) +diff -ruN proll_18.orig/qemu/head.S proll-patch-15/qemu/head.S +--- proll_18.orig/qemu/head.S 1970-01-01 00:00:00.000000000 +0000 ++++ proll-patch-15/qemu/head.S 2005-07-12 22:24:17.000000000 +0000 +@@ -0,0 +1,543 @@ ++/** ++ ** Standalone startup code for Linux PROM emulator. ++ ** Copyright 1999 Pete A. Zaitcev ++ ** This code is licensed under GNU General Public License. ++ **/ ++/* ++ * $Id: proll.patch,v 1.6 2005/11/11 00:24:57 bellard Exp $ ++ */ ++ ++#include ++#include ++#include ++/* #include */ /* Trap entries. Do not use. */ ++#include "phys_jj.h" ++ ++#define C_LABEL(name) name ++#define REGWIN_SZ 0x40 ++ ++#define WRITE_PAUSE nop; nop; nop; /* Have to do this after %wim/%psr chg */ ++ ++ /* 22 is 24-2, (va)>>(SRMMU_PGDIR_SHIFT-PTESIZESHFT) */ ++#define VATOPGDOFF(va) (((va)>>22)&0x3FC) ++#define VATOPMDOFF(va) (((va)>>16)&0xFC) ++ ++#define NOP_INSN 0x01000000 /* Used to patch sparc_save_state */ ++ ++/* Here are some trap goodies */ ++ ++#if 0 ++/* Generic trap entry. */ ++#define TRAP_ENTRY(type, label) \ ++ rd %psr, %l0; b label; rd %wim, %l3; nop; ++#endif ++ ++/* Data/text faults. */ ++#define SRMMU_TFAULT rd %psr, %l0; rd %wim, %l3; b C_LABEL(srmmu_fault); mov 1, %l7; ++#define SRMMU_DFAULT rd %psr, %l0; rd %wim, %l3; b C_LABEL(srmmu_fault); mov 0, %l7; ++ ++#if 0 ++/* This is for traps we should NEVER get. */ ++#define BAD_TRAP(num) \ ++ rd %psr, %l0; mov num, %l7; b bad_trap_handler; rd %wim, %l3; ++ ++/* This is for traps when we want just skip the instruction which caused it */ ++#define SKIP_TRAP(type, name) \ ++ jmpl %l2, %g0; rett %l2 + 4; nop; nop; ++ ++/* Notice that for the system calls we pull a trick. We load up a ++ * different pointer to the system call vector table in %l7, but call ++ * the same generic system call low-level entry point. The trap table ++ * entry sequences are also HyperSparc pipeline friendly ;-) ++ */ ++ ++/* Software trap for Linux system calls. */ ++#define LINUX_SYSCALL_TRAP \ ++ sethi %hi(C_LABEL(sys_call_table)), %l7; \ ++ or %l7, %lo(C_LABEL(sys_call_table)), %l7; \ ++ b linux_sparc_syscall; \ ++ rd %psr, %l0; ++ ++/* Software trap for SunOS4.1.x system calls. */ ++#define SUNOS_SYSCALL_TRAP \ ++ rd %psr, %l0; \ ++ sethi %hi(C_LABEL(sunos_sys_table)), %l7; \ ++ b linux_sparc_syscall; \ ++ or %l7, %lo(C_LABEL(sunos_sys_table)), %l7; ++ ++/* Software trap for Slowaris system calls. */ ++#define SOLARIS_SYSCALL_TRAP \ ++ b solaris_syscall; \ ++ rd %psr, %l0; \ ++ nop; \ ++ nop; ++ ++#define INDIRECT_SOLARIS_SYSCALL(x) \ ++ mov x, %g1; \ ++ b solaris_syscall; \ ++ rd %psr, %l0; \ ++ nop; ++ ++#define BREAKPOINT_TRAP \ ++ b breakpoint_trap; \ ++ rd %psr,%l0; \ ++ nop; \ ++ nop; ++ ++/* Software trap for Sparc-netbsd system calls. */ ++#define NETBSD_SYSCALL_TRAP \ ++ sethi %hi(C_LABEL(sys_call_table)), %l7; \ ++ or %l7, %lo(C_LABEL(sys_call_table)), %l7; \ ++ b bsd_syscall; \ ++ rd %psr, %l0; ++ ++/* The Get Condition Codes software trap for userland. */ ++#define GETCC_TRAP \ ++ b getcc_trap_handler; mov %psr, %l0; nop; nop; ++ ++/* The Set Condition Codes software trap for userland. */ ++#define SETCC_TRAP \ ++ b setcc_trap_handler; mov %psr, %l0; nop; nop; ++ ++/* This is for hard interrupts from level 1-14, 15 is non-maskable (nmi) and ++ * gets handled with another macro. ++ */ ++#define TRAP_ENTRY_INTERRUPT(int_level) \ ++ mov int_level, %l7; rd %psr, %l0; b real_irq_entry; rd %wim, %l3; ++ ++/* NMI's (Non Maskable Interrupts) are special, you can't keep them ++ * from coming in, and basically if you get one, the shows over. ;( ++ * On the sun4c they are usually asynchronous memory errors, on the ++ * the sun4m they could be either due to mem errors or a software ++ * initiated interrupt from the prom/kern on an SMP box saying "I ++ * command you to do CPU tricks, read your mailbox for more info." ++ */ ++#define NMI_TRAP \ ++ rd %wim, %l3; b linux_trap_nmi_sun4c; mov %psr, %l0; nop; ++ ++#endif ++ ++/* Window overflows/underflows are special and we need to try to be as ++ * efficient as possible here.... ++ */ ++#define WINDOW_SPILL \ ++ rd %psr, %l0; rd %wim, %l3; b spill_window_entry; nop; ++ ++#define WINDOW_FILL \ ++ rd %psr, %l0; rd %wim, %l3; b fill_window_entry; nop; ++ ++#define STUB_TRAP ba stub_trap; nop; nop; nop; ++ ++#define TRAP_ENTRY(a,b) STUB_TRAP ++#define SKIP_TRAP(a,b) STUB_TRAP ++#define SUNOS_SYSCALL_TRAP STUB_TRAP ++#define SOLARIS_SYSCALL_TRAP STUB_TRAP ++#define NETBSD_SYSCALL_TRAP STUB_TRAP ++#define LINUX_SYSCALL_TRAP STUB_TRAP ++#define BREAKPOINT_TRAP STUB_TRAP ++#define NMI_TRAP STUB_TRAP ++#define GETCC_TRAP STUB_TRAP ++#define SETCC_TRAP STUB_TRAP ++#define BAD_TRAP(n) STUB_TRAP ++#define TRAP_ENTRY_INTERRUPT(i) STUB_TRAP ++#define INDIRECT_SOLARIS_SYSCALL(i) STUB_TRAP ++ ++ .section ".text" ++ .globl start, _start ++_start: ++start: ++ .globl spill_window_entry, fill_window_entry ++ ++#define EXPORT_TRAP(trap) \ ++ .globl trap; \ ++ .type trap,function; \ ++ .size trap, 16 ++ ++EXPORT_TRAP(t_zero) ++EXPORT_TRAP(t_wovf) ++EXPORT_TRAP(t_wunf) ++EXPORT_TRAP(t_irq1) ++EXPORT_TRAP(t_irq2) ++EXPORT_TRAP(t_irq3) ++EXPORT_TRAP(t_irq4) ++EXPORT_TRAP(t_irq5) ++EXPORT_TRAP(t_irq6) ++EXPORT_TRAP(t_irq7) ++EXPORT_TRAP(t_irq8) ++EXPORT_TRAP(t_irq9) ++EXPORT_TRAP(t_irq10) ++EXPORT_TRAP(t_irq11) ++EXPORT_TRAP(t_irq12) ++EXPORT_TRAP(t_irq13) ++EXPORT_TRAP(t_irq14) ++EXPORT_TRAP(t_irq15) ++ ++C_LABEL(trapbase): ++t_zero: b goprol; nop; nop; nop; ++t_tflt: SRMMU_TFAULT /* Inst. Access Exception */ ++t_bins: TRAP_ENTRY(0x2, bad_instruction) /* Illegal Instruction */ ++t_pins: TRAP_ENTRY(0x3, priv_instruction) /* Privileged Instruction */ ++t_fpd: TRAP_ENTRY(0x4, fpd_trap_handler) /* Floating Point Disabled */ ++t_wovf: WINDOW_SPILL /* Window Overflow */ ++t_wunf: WINDOW_FILL /* Window Underflow */ ++t_mna: TRAP_ENTRY(0x7, mna_handler) /* Memory Address Not Aligned */ ++t_fpe: TRAP_ENTRY(0x8, fpe_trap_handler) /* Floating Point Exception */ ++t_dflt: SRMMU_DFAULT /* Data Miss Exception */ ++t_tio: TRAP_ENTRY(0xa, do_tag_overflow) /* Tagged Instruction Ovrflw */ ++t_wpt: TRAP_ENTRY(0xb, do_watchpoint) /* Watchpoint Detected */ ++t_badc: BAD_TRAP(0xc) BAD_TRAP(0xd) BAD_TRAP(0xe) BAD_TRAP(0xf) BAD_TRAP(0x10) ++t_irq1: TRAP_ENTRY_INTERRUPT(1) /* IRQ Software/SBUS Level 1 */ ++t_irq2: TRAP_ENTRY_INTERRUPT(2) /* IRQ SBUS Level 2 */ ++t_irq3: TRAP_ENTRY_INTERRUPT(3) /* IRQ SCSI/DMA/SBUS Level 3 */ ++t_irq4: TRAP_ENTRY_INTERRUPT(4) /* IRQ Software Level 4 */ ++t_irq5: TRAP_ENTRY_INTERRUPT(5) /* IRQ SBUS/Ethernet Level 5 */ ++t_irq6: TRAP_ENTRY_INTERRUPT(6) /* IRQ Software Level 6 */ ++t_irq7: TRAP_ENTRY_INTERRUPT(7) /* IRQ Video/SBUS Level 5 */ ++t_irq8: TRAP_ENTRY_INTERRUPT(8) /* IRQ SBUS Level 6 */ ++t_irq9: TRAP_ENTRY_INTERRUPT(9) /* IRQ SBUS Level 7 */ ++t_irq10:TRAP_ENTRY_INTERRUPT(10) /* IRQ Timer #1 (one we use) */ ++t_irq11:TRAP_ENTRY_INTERRUPT(11) /* IRQ Floppy Intr. */ ++t_irq12:TRAP_ENTRY_INTERRUPT(12) /* IRQ Zilog serial chip */ ++t_irq13:TRAP_ENTRY_INTERRUPT(13) /* IRQ Audio Intr. */ ++t_irq14:TRAP_ENTRY_INTERRUPT(14) /* IRQ Timer #2 */ ++t_nmi: NMI_TRAP /* Level 15 (NMI) */ ++t_racc: TRAP_ENTRY(0x20, do_reg_access) /* General Register Access Error */ ++t_iacce:BAD_TRAP(0x21) /* Instr Access Error */ ++t_bad22:BAD_TRAP(0x22) BAD_TRAP(0x23) ++t_cpdis:TRAP_ENTRY(0x24, do_cp_disabled) /* Co-Processor Disabled */ ++t_uflsh:SKIP_TRAP(0x25, unimp_flush) /* Unimplemented FLUSH inst. */ ++t_bad26:BAD_TRAP(0x26) BAD_TRAP(0x27) ++t_cpexc:TRAP_ENTRY(0x28, do_cp_exception) /* Co-Processor Exception */ ++t_dacce:SRMMU_DFAULT /* Data Access Error */ ++t_hwdz: TRAP_ENTRY(0x2a, do_hw_divzero) /* Division by zero, you lose... */ ++t_dserr:BAD_TRAP(0x2b) /* Data Store Error */ ++t_daccm:BAD_TRAP(0x2c) /* Data Access MMU-Miss */ ++t_bad2d: BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) ++ BAD_TRAP(0x30) BAD_TRAP(0x31) BAD_TRAP(0x32) BAD_TRAP(0x33) ++ BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36) BAD_TRAP(0x37) ++ BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b) ++t_iaccm:BAD_TRAP(0x3c) /* Instr Access MMU-Miss */ ++ BAD_TRAP(0x3d) BAD_TRAP(0x3e) BAD_TRAP(0x3f) ++ BAD_TRAP(0x40) BAD_TRAP(0x41) BAD_TRAP(0x42) BAD_TRAP(0x43) ++ BAD_TRAP(0x44) BAD_TRAP(0x45) BAD_TRAP(0x46) BAD_TRAP(0x47) ++ BAD_TRAP(0x48) BAD_TRAP(0x49) BAD_TRAP(0x4a) BAD_TRAP(0x4b) ++ BAD_TRAP(0x4c) BAD_TRAP(0x4d) BAD_TRAP(0x4e) BAD_TRAP(0x4f) ++ BAD_TRAP(0x50) BAD_TRAP(0x51) BAD_TRAP(0x52) BAD_TRAP(0x53) ++ BAD_TRAP(0x54) BAD_TRAP(0x55) BAD_TRAP(0x56) BAD_TRAP(0x57) ++ BAD_TRAP(0x58) BAD_TRAP(0x59) BAD_TRAP(0x5a) BAD_TRAP(0x5b) ++ BAD_TRAP(0x5c) BAD_TRAP(0x5d) BAD_TRAP(0x5e) BAD_TRAP(0x5f) ++ BAD_TRAP(0x60) BAD_TRAP(0x61) BAD_TRAP(0x62) BAD_TRAP(0x63) ++ BAD_TRAP(0x64) BAD_TRAP(0x65) BAD_TRAP(0x66) BAD_TRAP(0x67) ++ BAD_TRAP(0x68) BAD_TRAP(0x69) BAD_TRAP(0x6a) BAD_TRAP(0x6b) ++ BAD_TRAP(0x6c) BAD_TRAP(0x6d) BAD_TRAP(0x6e) BAD_TRAP(0x6f) ++ BAD_TRAP(0x70) BAD_TRAP(0x71) BAD_TRAP(0x72) BAD_TRAP(0x73) ++ BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) ++ BAD_TRAP(0x78) BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) ++ BAD_TRAP(0x7c) BAD_TRAP(0x7d) BAD_TRAP(0x7e) BAD_TRAP(0x7f) ++t_sunos:SUNOS_SYSCALL_TRAP /* SunOS System Call */ ++t_sbkpt:BREAKPOINT_TRAP /* Software Breakpoint/KGDB */ ++t_divz: BAD_TRAP(0x82) /* Divide by zero trap */ ++t_flwin:TRAP_ENTRY(0x83, do_flush_windows) /* Flush Windows Trap */ ++t_clwin:BAD_TRAP(0x84) /* Clean Windows Trap */ ++t_rchk: BAD_TRAP(0x85) /* Range Check */ ++t_funal:BAD_TRAP(0x86) /* Fix Unaligned Access Trap */ ++t_iovf: BAD_TRAP(0x87) /* Integer Overflow Trap */ ++t_slowl:SOLARIS_SYSCALL_TRAP /* Slowaris System Call */ ++t_netbs:NETBSD_SYSCALL_TRAP /* Net-B.S. System Call */ ++t_bad8a:BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c) BAD_TRAP(0x8d) ++ BAD_TRAP(0x8e) BAD_TRAP(0x8f) ++t_linux:LINUX_SYSCALL_TRAP /* Linux System Call */ ++t_bad91:BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) ++ BAD_TRAP(0x94) BAD_TRAP(0x95) BAD_TRAP(0x96) BAD_TRAP(0x97) ++ BAD_TRAP(0x98) BAD_TRAP(0x99) BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e) BAD_TRAP(0x9f) ++t_getcc:GETCC_TRAP /* Get Condition Codes */ ++t_setcc:SETCC_TRAP /* Set Condition Codes */ ++t_bada2:BAD_TRAP(0xa2) BAD_TRAP(0xa3) ++ BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6) ++t_slowi:INDIRECT_SOLARIS_SYSCALL(156) ++ BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab) ++ BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) ++ BAD_TRAP(0xb0) BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) ++ BAD_TRAP(0xb4) BAD_TRAP(0xb5) BAD_TRAP(0xb6) BAD_TRAP(0xb7) ++ BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba) BAD_TRAP(0xbb) ++ BAD_TRAP(0xbc) BAD_TRAP(0xbd) BAD_TRAP(0xbe) BAD_TRAP(0xbf) ++t_badc0:BAD_TRAP(0xc0) BAD_TRAP(0xc1) BAD_TRAP(0xc2) BAD_TRAP(0xc3) ++ BAD_TRAP(0xc4) BAD_TRAP(0xc5) BAD_TRAP(0xc6) BAD_TRAP(0xc7) ++ BAD_TRAP(0xc8) BAD_TRAP(0xc9) BAD_TRAP(0xca) BAD_TRAP(0xcb) ++ BAD_TRAP(0xcc) BAD_TRAP(0xcd) BAD_TRAP(0xce) BAD_TRAP(0xcf) ++ BAD_TRAP(0xd0) BAD_TRAP(0xd1) BAD_TRAP(0xd2) BAD_TRAP(0xd3) ++t_badd4:BAD_TRAP(0xd4) BAD_TRAP(0xd5) BAD_TRAP(0xd6) BAD_TRAP(0xd7) ++ BAD_TRAP(0xd8) BAD_TRAP(0xd9) BAD_TRAP(0xda) BAD_TRAP(0xdb) ++ BAD_TRAP(0xdc) BAD_TRAP(0xdd) BAD_TRAP(0xde) BAD_TRAP(0xdf) ++ BAD_TRAP(0xe0) BAD_TRAP(0xe1) BAD_TRAP(0xe2) BAD_TRAP(0xe3) ++ BAD_TRAP(0xe4) BAD_TRAP(0xe5) BAD_TRAP(0xe6) BAD_TRAP(0xe7) ++t_bade8:BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) ++ BAD_TRAP(0xec) BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) ++ BAD_TRAP(0xf0) BAD_TRAP(0xf1) BAD_TRAP(0xf2) BAD_TRAP(0xf3) ++ BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6) BAD_TRAP(0xf7) ++ BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb) ++t_badfc:BAD_TRAP(0xfc) BAD_TRAP(0xfd) ++dbtrap: BAD_TRAP(0xfe) /* Debugger/PROM breakpoint #1 */ ++dbtrap2:BAD_TRAP(0xff) /* Debugger/PROM breakpoint #2 */ ++ ++stub_trap: ++ set (PHYS_JJ_TCX_FB + 0xbf0), %g5 /* 2 cells from side */ ++ set 0x00ffffff, %g4 ++ sta %g4, [%g5] ASI_M_BYPASS ++1: ba 1b; nop ++ ++ .section ".bss" ++ .align 8 ++bss_start: ++ .align 0x1000 ! PAGE_SIZE ++ .globl C_LABEL(bootup_user_stack) ++ .type bootup_user_stack,#object ++ .size bootup_user_stack,0x2000 ++C_LABEL(bootup_user_stack): .skip 0x2000 ++ ++ .section ".text" ++ .register %g2, #scratch ++ .register %g3, #scratch ++ .register %g6, #scratch ++ .register %g7, #scratch ++ ++goprol: ++ ! %g1 contains end of memory ++ set PHYS_JJ_EEPROM + 0x30, %g1 ++ lda [%g1] ASI_M_BYPASS, %g1 ++ ! map PROLDATA to PROLBASE+PROLSIZE to end of ram ++ set PROLSIZE+0x1000-PROLDATA+PROLBASE, %g2 ! add 0x1000 for temp tables ++ sub %g1, %g2, %g2 ! start of private memory ++ srl %g2, 0x4, %g7 ! ctx table at s+0x0 ++ add %g2, 0x400, %g3 ! l1 table at s+0x400 ++ srl %g3, 0x4, %g3 ++ or %g3, 0x1, %g3 ++ sta %g3, [%g2] ASI_M_BYPASS ++ add %g2, 0x400, %g2 ! s+0x400 ++ add %g2, 0x800, %g3 ! l2 table for ram (00xxxxxx) at s+0x800 ++ srl %g3, 0x4, %g3 ++ or %g3, 0x1, %g3 ++ sta %g3, [%g2] ASI_M_BYPASS ++ add %g2, 0x500, %g3 ! l2 table for rom (ffxxxxxx) at s+0x900 ++ add %g2, 0x3fc, %g2 ! s+0x7fc ++ srl %g3, 0x4, %g3 ++ or %g3, 0x1, %g3 ++ sta %g3, [%g2] ASI_M_BYPASS ++ add %g2, 0x4, %g2 ! s+0x800 ++ set ((7 << 2) | 2), %g3 ! 7 = U: --- S: RWX (main memory) ++ sta %g3, [%g2] ASI_M_BYPASS ++ add %g2, 0x200, %g3 ! l3 table for rom at s+0xa00 ++ add %g2, 0x1d0, %g2 ! s+0x9d0 ++ srl %g3, 0x4, %g3 ++ or %g3, 0x1, %g3 ++ sta %g3, [%g2] ASI_M_BYPASS ++ add %g2, 0x30, %g2 ! s+0xa00 ++ ++ set PROLBASE, %g3 ++ set 0x1000, %g5 ++ set (PROLDATA-PROLBASE)/0x1000, %g6 ! # of .text pages ++1: srl %g3, 0x4, %g4 ++ or %g4, ((7 << 2) | 2), %g4 ! 4 = U: --X S: --X (rom, execute only) ++ sta %g4, [%g2] ASI_M_BYPASS ++ add %g2, 4, %g2 ++ add %g3, %g5, %g3 ++ deccc %g6 ++ bne 1b ++ nop ++#if 0 ++ set (PROLDATA-PROLRODATA)/0x1000, %g6 ! # of .rodata pages ++1: srl %g3, 0x4, %g4 ++ or %g4, ((0 << 2) | 2), %g4 ! 0 = U: R-- S: R-- (rom, read only) ++ sta %g4, [%g2] ASI_M_BYPASS ++ add %g2, 4, %g2 ++ add %g3, %g5, %g3 ++ deccc %g6 ++ bne 1b ++ nop ++#endif ++ set (PROLBASE+PROLSIZE-PROLDATA)/0x1000, %g6 ! # of .bss pages ++ set 0x1000, %g4 ++ sll %g7, 0x4, %g3 ++ add %g4, %g3, %g3 ++1: srl %g3, 0x4, %g4 ++ or %g4, ((7 << 2) | 2), %g4 ! 5 = U: R-- S: RW- (data area, read/write) ++ sta %g4, [%g2] ASI_M_BYPASS ++ add %g2, 4, %g2 ++ add %g3, %g5, %g3 ++ deccc %g6 ++ bne 1b ++ nop ++ ++ mov %g1, %g3 ++ ++ set AC_M_CTPR, %g2 ++ sta %g7, [%g2] ASI_M_MMUREGS ! set ctx table ptr ++ set 1, %g1 ++ sta %g1, [%g0] ASI_M_MMUREGS ! enable mmu ++ ++ /* ++ * The code which enables traps is a simplified version of ++ * kernel head.S. ++ * ++ * We know number of windows as 8 so we do not calculate them. ++ * The deadwood is here for any case. ++ */ ++ ++ /* Turn on Supervisor, EnableFloating, and all the PIL bits. ++ * Also puts us in register window zero with traps off. ++ */ ++ set (PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2 ++ wr %g2, 0x0, %psr ++ WRITE_PAUSE ++ ++ /* I want a kernel stack NOW! */ ++ set C_LABEL(bootup_user_stack), %g1 ++ set (0x2000 - REGWIN_SZ), %g2 ++ add %g1, %g2, %sp ++ mov 0, %fp /* And for good luck */ ++ ++ /* Zero out our BSS section. */ ++ set C_LABEL(bss_start) , %o0 ! First address of BSS ++ set C_LABEL(end) , %o1 ! Last address of BSS ++ ba 2f ++ nop ++1: ++ st %g0, [%o0] ++2: ++ subcc %o0, %o1, %g0 ++ bl 1b ++ add %o0, 0x4, %o0 ++ ++ mov 2, %g1 ++ wr %g1, 0x0, %wim ! make window 1 invalid ++ WRITE_PAUSE ++ ++#if 0 ++ wr %g0, 0x0, %wim ++ WRITE_PAUSE ++ save ++ rd %psr, %g3 ++ restore ++ and %g3, PSR_CWP, %g3 ++ add %g3, 0x1, %g3 ++#else ++ or %g0, 8, %g3 ++#endif ++ ++#if 0 ++ sethi %hi( C_LABEL(cputyp) ), %o0 ++ st %g7, [%o0 + %lo( C_LABEL(cputyp) )] ++ ++ sethi %hi( C_LABEL(nwindows) ), %g4 ++ st %g3, [%g4 + %lo( C_LABEL(nwindows) )] ++ ++ sub %g3, 0x1, %g3 ++ sethi %hi( C_LABEL(nwindowsm1) ), %g4 ++ st %g3, [%g4 + %lo( C_LABEL(nwindowsm1) )] ++#endif ++ ++ /* Here we go, start using Linux's trap table... */ ++ set C_LABEL(trapbase), %g3 ++ wr %g3, 0x0, %tbr ++ WRITE_PAUSE ++ ++ /* Finally, turn on traps so that we can call c-code. */ ++ rd %psr, %g3 ++ wr %g3, 0x0, %psr ++ WRITE_PAUSE ++ ++ wr %g3, PSR_ET, %psr ++ WRITE_PAUSE ++ ++ .globl prolmain ++ call C_LABEL(prolmain) ++ nop ++ ++3: ++ b 3b ++ nop ++ ++/* ++ * Memory access trap handler ++ * %l0 program %psr from trap table entry ++ * %l1 program %pc from hardware ++ * %l2 program %npc from hardware ++ * %l3 program %wim from trap table entry ++ * %l4 ++ * %l5 ++ * %l6 ++ * %l7 text flag from trap table entry ++ */ ++ ++ .section ".text" ++ .globl srmmu_fault ++C_LABEL(srmmu_fault): ++ ++ set AC_M_SFAR, %l6 ++ set AC_M_SFSR, %l5 ++ lda [%l6] ASI_M_MMUREGS, %l6 ++ lda [%l5] ASI_M_MMUREGS, %l5 ++ ++ set ignore_fault, %l5 ++ ld [%l5], %l5 ++ subcc %l5, %g0, %g0 /* NULL pointer trap faults always */ ++ be 3f ++ nop ++ subcc %l5, %l6, %g0 ++ be 2f ++ nop ++3: ++ ++ set (PHYS_JJ_TCX_FB + 0xbf0), %g5 /* 2 cells from side */ ++ set 0x00ffffff, %g4 ++ sta %g4, [%g5] ASI_M_BYPASS ++ add %g5, 8, %g5 /* On right side */ ++ sta %g4, [%g5] ASI_M_BYPASS ++1: ba 1b; nop ++ ++2: ++ set C_LABEL(fault_ignored), %l5 ++ mov 1, %l6 ++ st %l6, [%l5] ++ ++ /* ++ * Skip the faulting instruction. ++ * I think it works when next instruction is a branch even. ++ */ ++ or %l2, 0, %l1 ++ add %l2, 4, %l2 ++ ++ wr %l0, 0, %psr ++ WRITE_PAUSE ++ jmp %l1 ++ rett %l2 ++ ++/* ++ * Slow external versions of st_bypass and ld_bypass. ++ * rconsole.c uses inlines. We call these in places which are not speed ++ * critical, to avoid compiler bugs. ++ */ ++ .globl C_LABEL(st_bypass) ++C_LABEL(st_bypass): ++ retl ++ sta %o1, [%o0] ASI_M_BYPASS ++ .globl C_LABEL(ld_bypass) ++C_LABEL(ld_bypass): ++ retl ++ lda [%o0] ASI_M_BYPASS, %o0 ++ .globl C_LABEL(sth_bypass) ++C_LABEL(sth_bypass): ++ retl ++ stha %o1, [%o0] ASI_M_BYPASS ++ .globl C_LABEL(ldh_bypass) ++C_LABEL(ldh_bypass): ++ retl ++ lduha [%o0] ASI_M_BYPASS, %o0 ++ .globl C_LABEL(stb_bypass) ++C_LABEL(stb_bypass): ++ retl ++ stba %o1, [%o0] ASI_M_BYPASS ++ .globl C_LABEL(ldb_bypass) ++C_LABEL(ldb_bypass): ++ retl ++ lduba [%o0] ASI_M_BYPASS, %o0 +diff -ruN proll_18.orig/qemu/main.c proll-patch-15/qemu/main.c +--- proll_18.orig/qemu/main.c 1970-01-01 00:00:00.000000000 +0000 ++++ proll-patch-15/qemu/main.c 2005-08-14 10:07:48.000000000 +0000 +@@ -0,0 +1,185 @@ ++/** ++ ** Proll (PROM replacement) ++ ** Copyright 1999 Pete Zaitcev ++ ** This code is licensed under GNU General Public License. ++ **/ ++#include ++ ++// #include ++#include ++#include "pgtsrmmu.h" ++#include "iommu.h" /* Typical SBus IOMMU for sun4m */ ++#include "phys_jj.h" ++#include "vconsole.h" ++#include "version.h" ++#include /* __P() */ ++#include /* init_net() */ ++#include /* we are a provider for part of this. */ ++#include /* myipaddr */ ++#include ++#include /* our own prototypes */ ++ ++void *init_openprom_qemu(int bankc, struct bank *bankv, unsigned hiphybas, const char *cmdline, char boot_device, int nographic); ++int vcon_zs_init(struct vconterm *t, unsigned int a0); ++int vcon_zs_write(struct vconterm *t, char *data, int leng); ++int vcon_zs_getch(struct vconterm *t); ++void esp_probe(); ++int esp_boot(int unit); ++static void init_idprom(void); ++ ++struct vconterm dp0; ++struct mem cmem; /* Current memory, virtual */ ++struct mem cio; /* Current I/O space */ ++struct phym pmem; /* Current phys. mem. */ ++struct iommu ciommu; /* Our IOMMU on sun4m */ ++ ++static struct { ++ const char id[16]; ++ unsigned int version; ++ char pad1[0x1c]; // Pad to 0x30 ++ unsigned int ram_size; ++ char boot_device; ++ unsigned int load_addr, kernel_size; ++ unsigned int cmdline, cmdline_len; ++ char pad2[0x0c]; // Pad to 0x54 ++ unsigned short width, height, depth; ++} *hw_idprom; ++ ++int ignore_fault, fault_ignored; ++void *printk_fn, *getch_fn; ++unsigned int q_height, q_width; ++ ++/* ++ */ ++void prolmain() ++{ ++ static char fname[14]; ++ static struct banks bb; ++ unsigned int hiphybas; ++ const void *romvec; ++ unsigned int ram_size; ++ char nographic, bootdev; ++ ++ nographic = ldb_bypass(PHYS_JJ_EEPROM + 0x2F); ++ if (!nographic) { ++ q_width = ldh_bypass(PHYS_JJ_EEPROM + 0x54); ++ q_height = ldh_bypass(PHYS_JJ_EEPROM + 0x56); ++ vcon_init(&dp0, PHYS_JJ_TCX_FB); ++ printk_fn = vcon_write; ++ getch_fn = vcon_getch; ++ } ++ else { ++ vcon_zs_init(&dp0, 0x71100004); ++ printk_fn = vcon_zs_write; ++ getch_fn = vcon_zs_getch; ++ } ++ ++ ++ printk("PROLL %s QEMU\n", PROLL_VERSION_STRING); ++ ram_size = ld_bypass(PHYS_JJ_EEPROM + 0x30); ++ printk("%d MB total\n", ram_size/(1024*1024)); ++ ++ bb.nbanks = 1; ++ bb.bankv[0].start = 0; ++ bb.bankv[0].length = ram_size; ++ ++ hiphybas = ram_size - PROLSIZE; ++ ++ mem_init(&cmem, (char *) &_end, (char *)(PROLBASE+PROLSIZE)); ++ makepages(&pmem, hiphybas); ++ init_mmu_swift((unsigned int)pmem.pctp - PROLBASE + hiphybas); ++ ++ mem_init(&cio, (char *)(PROLBASE+PROLSIZE), ++ (char *)(PROLBASE+PROLSIZE+IOMAPSIZE)); ++ ++ iommu_init(&ciommu, hiphybas); ++ ++ /* ++ */ ++ init_idprom(); ++ printk("NVRAM: id %s version %d\n", hw_idprom->id, hw_idprom->version); ++ if (!nographic) ++ printk("Prom console: TCX %dx%d\n", q_width, q_height); ++ else ++ printk("Prom console: serial\n"); ++ sched_init(); ++ le_probe(); ++ init_net(); ++ esp_probe(); ++ ++ bootdev = hw_idprom->boot_device; ++ printk("Boot device: %c\n", bootdev); ++ if (hw_idprom->kernel_size > 0) { ++ printk("Kernel already loaded\n"); ++ } else if (bootdev == 'n') { ++ if (bootp() != 0) fatal(); ++ /* ++ * boot_rec.bp_file cannot be used because system PROM ++ * uses it to locate ourselves. If we load from boot_rec.bp_file, ++ * we will loop reloading PROLL over and over again. ++ * Thus we use traditional PROLL scheme HEXIPADDR.PROL (single L). ++ */ ++ xtoa(myipaddr, fname, 8); ++ fname[9] = '.'; ++ fname[10] = 'P'; ++ fname[11] = 'R'; ++ fname[12] = 'O'; ++ fname[13] = 'L'; ++ fname[14] = 0; ++ ++ if (load(boot_rec.bp_siaddr, fname) != 0) fatal(); ++ } else if (bootdev == 'c') { ++ if (esp_boot(0) != 0) fatal(); ++ } else if (bootdev == 'd') { ++ if (esp_boot(2) != 0) fatal(); ++ } ++ ++ romvec = init_openprom_qemu(bb.nbanks, bb.bankv, hiphybas, ++ (void *)hw_idprom->cmdline, hw_idprom->boot_device, nographic); ++ ++ printk("Memory used: virt 0x%x:0x%x[%dK] iomap 0x%x:0x%x\n", ++ PROLBASE, (int)cmem.curp, ((unsigned) cmem.curp - PROLBASE)/1024, ++ (int)cio.start, (int)cio.curp); ++ ++ { ++ void (*entry)(const void *, int, int, int, int) = (void *) hw_idprom->load_addr; ++ printk("Kernel loaded at 0x%x, size %dK, command line = '%s'\n", ++ *entry, hw_idprom->kernel_size/1024, hw_idprom->cmdline); ++ entry(romvec, 0, 0, 0, 0); ++ } ++ ++ mem_fini(&cmem); ++ vcon_fini(&dp0); ++} ++ ++/* ++ * dvma_alloc over iommu_alloc. ++ */ ++void *dvma_alloc(int size, unsigned int *pphys) ++{ ++ return iommu_alloc(&ciommu, size, pphys); ++} ++ ++/* ++ */ ++void udelay(__attribute__((unused)) unsigned long usecs) ++{ ++ // Qemu hardware is perfect and does not need any delays! ++} ++ ++static void init_idprom() ++{ ++ void *va_prom; ++ ++ if ((va_prom = map_io(PHYS_JJ_EEPROM, PHYS_JJ_EEPROM_SIZE)) == NULL) { ++ printk("init_idprom: cannot map eeprom\n"); ++ fatal(); ++ } ++ bcopy(va_prom + PHYS_JJ_IDPROM_OFF, idprom, IDPROM_SIZE); ++ /* ++ * hw_idprom is not used anywhere. ++ * It's just as we hate to leave hanging pointers (I/O page here). ++ */ ++ hw_idprom = va_prom; ++} ++ +diff -ruN proll_18.orig/qemu/openprom.c proll-patch-15/qemu/openprom.c +--- proll_18.orig/qemu/openprom.c 1970-01-01 00:00:00.000000000 +0000 ++++ proll-patch-15/qemu/openprom.c 2005-11-07 20:11:04.000000000 +0000 +@@ -0,0 +1,910 @@ ++/* ++ * PROM interface support ++ * Copyright 1996 The Australian National University. ++ * Copyright 1996 Fujitsu Laboratories Limited ++ * Copyright 1999 Pete A. Zaitcev ++ * This software may be distributed under the terms of the Gnu ++ * Public License version 2 or later ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "phys_jj.h" ++ ++//#define DEBUG_OBP ++ ++#define PAGE_SIZE 4096 ++ ++struct property { ++ const char *name; ++ const char *value; ++ int length; ++}; ++ ++struct node { ++ const struct property *properties; ++ /* short */ const int sibling; ++ /* short */ const int child; ++}; ++ ++static int obp_nextnode(int node); ++static int obp_child(int node); ++static int obp_proplen(int node, char *name); ++static int obp_getprop(int node, char *name, char *val); ++static int obp_setprop(int node, char *name, char *val, int len); ++static const char *obp_nextprop(int node, char *name); ++ ++static char obp_idprom[IDPROM_SIZE]; ++static const struct property null_properties = { NULL, NULL, -1 }; ++static const int prop_true = -1; ++ ++static struct property propv_root[7]; ++ ++static const struct property propv_root_templ[] = { ++ {"name", "SUNW,SparcStation-5", sizeof("SUNW,SparcStation-5") }, ++ {"idprom", obp_idprom, IDPROM_SIZE}, ++ {"banner-name", "SparcStation", sizeof("SparcStation")}, ++ {"compatible", "sun4m", 6}, ++}; ++ ++static const int prop_iommu_reg[] = { ++ 0x0, 0x10000000, 0x00000300, ++}; ++static const struct property propv_iommu[] = { ++ {"name", "iommu", sizeof("iommu")}, ++ {"reg", (char*)&prop_iommu_reg[0], sizeof(prop_iommu_reg) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_sbus_ranges[] = { ++ 0x0, 0x0, 0x0, 0x30000000, 0x10000000, ++ 0x1, 0x0, 0x0, 0x40000000, 0x10000000, ++ 0x2, 0x0, 0x0, 0x50000000, 0x10000000, ++ 0x3, 0x0, 0x0, 0x60000000, 0x10000000, ++ 0x4, 0x0, 0x0, 0x70000000, 0x10000000, ++}; ++static const struct property propv_sbus[] = { ++ {"name", "sbus", 5}, ++ {"ranges", (char*)&prop_sbus_ranges[0], sizeof(prop_sbus_ranges)}, ++ {"device_type", "hierarchical", sizeof("hierarchical") }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_tcx_regs[] = { ++ 0x2, 0x00800000, 0x00100000, ++ 0x2, 0x02000000, 0x00000001, ++ 0x2, 0x04000000, 0x00800000, ++ 0x2, 0x06000000, 0x00800000, ++ 0x2, 0x0a000000, 0x00000001, ++ 0x2, 0x0c000000, 0x00000001, ++ 0x2, 0x0e000000, 0x00000001, ++ 0x2, 0x00700000, 0x00001000, ++ 0x2, 0x00200000, 0x00000004, ++ 0x2, 0x00300000, 0x0000081c, ++ 0x2, 0x00000000, 0x00010000, ++ 0x2, 0x00240000, 0x00000004, ++ 0x2, 0x00280000, 0x00000001, ++}; ++ ++#if 1 /* Zaitcev */ ++static const int pixfreq = 0x03dfd240; ++static const int hbporch = 0xa0; ++static const int vfreq = 0x3c; ++#endif ++#if 0 /* Kevin Boone - 70Hz refresh */ ++static const int pixfreq = 0x047868C0; ++static const int hbporch = 0x90; ++static const int vfreq = 0x46; ++#endif ++ ++static const int vbporch = 0x1d; ++static const int vsync = 0x6; ++static const int hsync = 0x88; ++static const int vfporch = 0x3; ++static const int hfporch = 0x18; ++static const int height = 0x300; ++static const int width = 0x400; ++static const int linebytes = 0x400; ++static const int depth = 24; ++static const int tcx_intr[] = { 5, 0 }; ++static const int tcx_interrupts = 5; ++static const struct property propv_sbus_tcx[] = { ++ {"name", "SUNW,tcx", sizeof("SUNW,tcx")}, ++ {"vbporch", (char*)&vbporch, sizeof(int)}, ++ {"hbporch", (char*)&hbporch, sizeof(int)}, ++ {"vsync", (char*)&vsync, sizeof(int)}, ++ {"hsync", (char*)&hsync, sizeof(int)}, ++ {"vfporch", (char*)&vfporch, sizeof(int)}, ++ {"hfporch", (char*)&hfporch, sizeof(int)}, ++ {"pixfreq", (char*)&pixfreq, sizeof(int)}, ++ {"vfreq", (char*)&vfreq, sizeof(int)}, ++ {"height", (char*)&height, sizeof(int)}, ++ {"width", (char*)&width, sizeof(int)}, ++ {"linebytes", (char*)&linebytes, sizeof(int)}, ++ {"depth", (char*)&depth, sizeof(int)}, ++ {"reg", (char*)&prop_tcx_regs[0], sizeof(prop_tcx_regs)}, ++ {"tcx-8-bit", 0, -1}, ++ {"intr", (char*)&tcx_intr[0], sizeof(tcx_intr)}, ++ {"interrupts", (char*)&tcx_interrupts, sizeof(tcx_interrupts)}, ++ {"device_type", "display", sizeof("display")}, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_cs4231_reg[] = { ++ 0x3, 0x0C000000, 0x00000040 ++}; ++static const int cs4231_interrupts = 5; ++static const int cs4231_intr[] = { 5, 0 }; ++ ++static const struct property propv_sbus_cs4231[] = { ++ {"name", "SUNW,CS4231", sizeof("SUNW,CS4231") }, ++ {"intr", (char*)&cs4231_intr[0], sizeof(cs4231_intr) }, ++ {"interrupts", (char*)&cs4231_interrupts, sizeof(cs4231_interrupts) }, ++ {"reg", (char*)&prop_cs4231_reg[0], sizeof(prop_cs4231_reg) }, ++ {"device_type", "serial", sizeof("serial") }, ++ {"alias", "audio", sizeof("audio") }, ++ {NULL, NULL, -1} ++}; ++ ++static const int cpu_nctx = NCTX_SWIFT; ++static const int cpu_cache_line_size = 0x20; ++static const int cpu_cache_nlines = 0x200; ++static const struct property propv_cpu[] = { ++ {"name", "STP1012PGA", sizeof("STP1012PGA") }, ++ {"device_type", "cpu", 4 }, ++ {"mmu-nctx", (char*)&cpu_nctx, sizeof(int)}, ++ {"cache-line-size", (char*)&cpu_cache_line_size, sizeof(int)}, ++ {"cache-nlines", (char*)&cpu_cache_nlines, sizeof(int)}, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_obio_ranges[] = { ++ 0x0, 0x0, 0x0, 0x71000000, 0x01000000, ++}; ++static const struct property propv_obio[] = { ++ {"name", "obio", 5 }, ++ {"ranges", (char*)&prop_obio_ranges[0], sizeof(prop_obio_ranges) }, ++ {"device_type", "hierarchical", sizeof("hierarchical") }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_auxio_reg[] = { ++ 0x0, 0x00900000, 0x00000001, ++}; ++static const struct property propv_obio_auxio[] = { ++ {"name", "auxio", sizeof("auxio") }, ++ {"reg", (char*)&prop_auxio_reg[0], sizeof(prop_auxio_reg) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_int_reg[] = { ++ 0x0, 0x00e00000, 0x00000010, ++ 0x0, 0x00e10000, 0x00000010, ++}; ++static const struct property propv_obio_int[] = { ++ {"name", "interrupt", sizeof("interrupt")}, ++ {"reg", (char*)&prop_int_reg[0], sizeof(prop_int_reg) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_cnt_reg[] = { ++ 0x0, 0x00d00000, 0x00000010, ++ 0x0, 0x00d10000, 0x00000010, ++}; ++static const struct property propv_obio_cnt[] = { ++ {"name", "counter", sizeof("counter")}, ++ {"reg", (char*)&prop_cnt_reg[0], sizeof(prop_cnt_reg) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_eeprom_reg[] = { ++ 0x0, 0x00200000, 0x00002000, ++}; ++static const struct property propv_obio_eep[] = { ++ {"name", "eeprom", sizeof("eeprom")}, ++ {"reg", (char*)&prop_eeprom_reg[0], sizeof(prop_eeprom_reg) }, ++ {"model", "mk48t08", sizeof("mk48t08")}, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_su_reg[] = { ++ 0x0, 0x003002f8, 0x00000008, ++}; ++static const struct property propv_obio_su[] = { ++ {"name", "su", sizeof("su")}, ++ {"reg", (char*)&prop_su_reg[0], sizeof(prop_su_reg) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_zs_intr[] = { 0x2c, 0x0 }; ++static const int prop_zs_reg[] = { ++ 0x0, 0x00000000, 0x00000008, ++}; ++static void *prop_zs_addr; ++static const int prop_zs_slave = 1; ++static const struct property propv_obio_zs[] = { ++ {"name", "zs", sizeof("zs")}, ++ {"reg", (char*)&prop_zs_reg[0], sizeof(prop_zs_reg) }, ++ {"slave", (char*)&prop_zs_slave, sizeof(prop_zs_slave) }, ++ {"device_type", "serial", sizeof("serial") }, ++ {"intr", (char*)&prop_zs_intr[0], sizeof(prop_zs_intr) }, ++ {"address", (char*)&prop_zs_addr, sizeof(prop_zs_addr) }, ++ {"keyboard", (char*)&prop_true, 0}, ++ {"mouse", (char*)&prop_true, 0}, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_zs1_intr[] = { 0x2c, 0x0 }; ++static const int prop_zs1_reg[] = { ++ 0x0, 0x00100000, 0x00000008, ++}; ++static void *prop_zs1_addr; ++static const int prop_zs1_slave = 0; ++static const struct property propv_obio_zs1[] = { ++ {"name", "zs", sizeof("zs")}, ++ {"reg", (char*)&prop_zs1_reg[0], sizeof(prop_zs1_reg) }, ++ {"slave", (char*)&prop_zs1_slave, sizeof(prop_zs1_slave) }, ++ {"device_type", "serial", sizeof("serial") }, ++ {"intr", (char*)&prop_zs1_intr[0], sizeof(prop_zs1_intr) }, ++ {"address", (char*)&prop_zs1_addr, sizeof(prop_zs1_addr) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_ledma_reg[] = { ++ 0x4, 0x08400010, 0x00000020, ++}; ++static const int prop_ledma_burst = 0x3f; ++static const struct property propv_sbus_ledma[] = { ++ {"name", "ledma", sizeof("ledma")}, ++ {"reg", (char*)&prop_ledma_reg[0], sizeof(prop_ledma_reg) }, ++ {"burst-sizes", (char*)&prop_ledma_burst, sizeof(int) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_le_reg[] = { ++ 0x4, 0x08c00000, 0x00000004, ++}; ++static const int prop_le_busmaster_regval = 0x7; ++static const int prop_le_intr[] = { 0x26, 0x0 }; ++static const struct property propv_sbus_ledma_le[] = { ++ {"name", "le", sizeof("le")}, ++ {"reg", (char*)&prop_le_reg[0], sizeof(prop_le_reg) }, ++ {"busmaster-regval", (char*)&prop_le_busmaster_regval, sizeof(int)}, ++ {"intr", (char*)&prop_le_intr[0], sizeof(prop_le_intr) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_espdma_reg[] = { ++ 0x4, 0x08400000, 0x00000010, ++}; ++ ++static const struct property propv_sbus_espdma[] = { ++ {"name", "espdma", sizeof("espdma")}, ++ {"reg", (char*)&prop_espdma_reg[0], sizeof(prop_espdma_reg) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_esp_reg[] = { ++ 0x4, 0x08800000, 0x00000040, ++}; ++static const int prop_esp_intr[] = { 0x24, 0x0 }; ++static const struct property propv_sbus_espdma_esp[] = { ++ {"name", "esp", sizeof("esp")}, ++ {"reg", (char*)&prop_esp_reg[0], sizeof(prop_esp_reg) }, ++ {"intr", (char*)&prop_esp_intr[0], sizeof(prop_esp_intr) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_bpp_reg[] = { ++ 0x4, 0x0c800000, 0x0000001c, ++}; ++static const int prop_bpp_intr[] = { 0x33, 0x0 }; ++static const struct property propv_sbus_bpp[] = { ++ {"name", "SUNW,bpp", sizeof("SUNW,bpp")}, ++ {"reg", (char*)&prop_bpp_reg[0], sizeof(prop_bpp_reg) }, ++ {"intr", (char*)&prop_bpp_intr[0], sizeof(prop_bpp_intr) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_apc_reg[] = { ++ 0x4, 0x0a000000, 0x00000010, ++}; ++static const struct property propv_sbus_apc[] = { ++ {"name", "xxxpower-management", sizeof("xxxpower-management")}, ++ {"reg", (char*)&prop_apc_reg[0], sizeof(prop_apc_reg) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_fd_intr[] = { 0x2b, 0x0 }; ++static const int prop_fd_reg[] = { ++ 0x0, 0x00400000, 0x0000000f, ++}; ++static const struct property propv_obio_fd[] = { ++ {"name", "SUNW,fdtwo", sizeof("SUNW,fdtwo")}, ++ {"reg", (char*)&prop_fd_reg[0], sizeof(prop_fd_reg) }, ++ {"device_type", "block", sizeof("block") }, ++ {"intr", (char*)&prop_fd_intr[0], sizeof(prop_fd_intr) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_pw_intr[] = { 0x22, 0x0 }; ++static const int prop_pw_reg[] = { ++ 0x0, 0x00910000, 0x00000001, ++}; ++static const struct property propv_obio_pw[] = { ++ {"name", "power", sizeof("power")}, ++ {"reg", (char*)&prop_pw_reg[0], sizeof(prop_pw_reg) }, ++ {"intr", (char*)&prop_pw_intr[0], sizeof(prop_pw_intr) }, ++ {NULL, NULL, -1} ++}; ++ ++static const int prop_cf_reg[] = { ++ 0x0, 0x00800000, 0x00000001, ++}; ++static const struct property propv_obio_cf[] = { ++ {"name", "slavioconfig", sizeof("slavioconfig")}, ++ {"reg", (char*)&prop_cf_reg[0], sizeof(prop_cf_reg) }, ++ {NULL, NULL, -1} ++}; ++ ++static const struct property propv_options[] = { ++ {"name", "options", sizeof("options")}, ++ {"screen-#columns", "80", sizeof("80")}, ++ {"screen-#rows", "25", sizeof("25")}, ++ {"tpe-link-test?", (char *)&prop_true, 0}, ++ {"ttya-mode", "9600,8,n,1,-", sizeof("9600,8,n,1,-")}, ++ {"ttya-ignore-cd", (char *)&prop_true, 0}, ++ {"ttya-rts-dtr-off", 0, -1}, ++ {"ttyb-mode", "9600,8,n,1,-", sizeof("9600,8,n,1,-")}, ++ {"ttyb-ignore-cd", (char *)&prop_true, 0}, ++ {"ttyb-rts-dtr-off", 0, -1}, ++ {NULL, NULL, -1} ++}; ++ ++static int prop_mem_reg[3]; ++static int prop_mem_avail[3]; ++ ++static const struct property propv_memory[] = { ++ {"name", "memory", sizeof("memory")}, ++ {"reg", (char*)&prop_mem_reg[0], sizeof(prop_mem_reg) }, ++ {"available", (char*)&prop_mem_avail[0], sizeof(prop_mem_avail) }, ++ {NULL, NULL, -1} ++}; ++ ++static int prop_vmem_avail[6]; ++ ++static const struct property propv_vmemory[] = { ++ {"name", "virtual-memory", sizeof("virtual-memory")}, ++ {"available", (char*)&prop_vmem_avail[0], sizeof(prop_vmem_avail) }, ++ {NULL, NULL, -1} ++}; ++ ++static const struct node nodes[] = { ++ { &null_properties, 1, 0 }, /* 0 = big brother of root */ ++ { propv_root, 0, 2 }, /* 1 "/" */ ++ { propv_iommu, 12, 3 }, /* 2 "/iommu" */ ++ { propv_sbus, 0, 4 }, /* 3 "/iommu/sbus" */ ++ { propv_sbus_tcx, 5, 0 }, /* 4 "/iommu/sbus/SUNW,tcx" */ ++ { propv_sbus_ledma, 7, 6 }, /* 5 "/iommu/sbus/ledma" */ ++ { propv_sbus_ledma_le, 0, 0 }, /* 6 "/iommu/sbus/ledma/le" */ ++ { propv_sbus_cs4231, 8, 0 }, /* 7 "/iommu/sbus/SUNW,CS4231 */ ++ { propv_sbus_bpp, 9, 0 }, /* 8 "/iommu/sbus/SUNW,bpp */ ++ { propv_sbus_espdma, 11, 10 }, /* 9 "/iommu/sbus/espdma" */ ++ { propv_sbus_espdma_esp, 0, 0 }, /* 10 "/iommu/sbus/espdma/esp" */ ++ { propv_sbus_apc, 0, 0 }, /* 11 "/iommu/sbus/power-management */ ++ { propv_cpu, 13, 0 }, /* 12 "/STP1012PGA" */ ++ { propv_obio, 23, 14 }, /* 13 "/obio" */ ++ { propv_obio_int, 15, 0 }, /* 14 "/obio/interrupt" */ ++ { propv_obio_cnt, 16, 0 }, /* 15 "/obio/counter" */ ++ { propv_obio_eep, 17, 0 }, /* 16 "/obio/eeprom" */ ++ { propv_obio_auxio, 18, 0 }, /* 17 "/obio/auxio" */ ++ { propv_obio_zs1, 19, 0 }, /* 18 "/obio/zs@0,100000" ++ Must be before zs@0,0! */ ++ { propv_obio_zs, 20, 0 }, /* 19 "/obio/zs@0,0" */ ++ { propv_obio_fd, 21, 0 }, /* 20 "/obio/SUNW,fdtwo" */ ++ { propv_obio_pw, 22, 0 }, /* 21 "/obio/power" */ ++ { propv_obio_cf, 0, 0 }, /* 22 "/obio/slavioconfig@0,800000" */ ++ { propv_options, 24, 0 }, /* 23 "/options" */ ++ { propv_memory, 25, 0 }, /* 24 "/memory" */ ++ { propv_vmemory, 0, 0 }, /* 25 "/virtual-memory" */ ++}; ++ ++static struct linux_mlist_v0 totphys[MAX_BANKS]; ++static struct linux_mlist_v0 totmap[1]; ++static struct linux_mlist_v0 totavail[MAX_BANKS]; ++ ++static struct linux_mlist_v0 *ptphys; ++static struct linux_mlist_v0 *ptmap; ++static struct linux_mlist_v0 *ptavail; ++ ++static const struct linux_nodeops nodeops0 = { ++ obp_nextnode, /* int (*no_nextnode)(int node); */ ++ obp_child, /* int (*no_child)(int node); */ ++ obp_proplen, /* int (*no_proplen)(int node, char *name); */ ++ obp_getprop, /* int (*no_getprop)(int node,char *name,char *val); */ ++ obp_setprop, /* int (*no_setprop)(int node, char *name, ++ char *val, int len); */ ++ obp_nextprop /* char * (*no_nextprop)(int node, char *name); */ ++}; ++ ++static struct linux_arguments_v0 obp_arg; ++static const struct linux_arguments_v0 * const obp_argp = &obp_arg; ++ ++static void (*synch_hook)(void); ++static char obp_stdin, obp_stdout; ++static int obp_fd_stdin, obp_fd_stdout; ++ ++static int obp_nbgetchar(void); ++static int obp_nbputchar(int ch); ++static void obp_reboot(char *); ++static void obp_abort(void); ++static void obp_halt(void); ++static int obp_devopen(char *str); ++static int obp_devclose(int dev_desc); ++static int obp_devread(int dev_desc, char *buf, int nbytes); ++static int obp_devwrite(int dev_desc, char *buf, int nbytes); ++static int obp_devseek(int dev_desc, int hi, int lo); ++static int obp_rdblkdev(int dev_desc, int num_blks, int blk_st, char *buf); ++static char *obp_dumb_mmap(char *va, int which_io, unsigned int pa, unsigned int size); ++static void obp_dumb_munmap(char *va, unsigned int size); ++static int obp_inst2pkg(int dev_desc); ++ ++static void doublewalk(unsigned ptab1, unsigned va) ++{ ++unsigned int proc_tablewalk(int ctx, unsigned int va); ++unsigned int mem_tablewalk(unsigned int pa, unsigned int va); ++ ++ proc_tablewalk(0, va); ++ if (ptab1 != 0) mem_tablewalk(ptab1, va); ++} ++ ++static struct linux_romvec romvec0; ++ ++struct fd { ++ int unit, part; ++ int offset; ++ int (*pread)(int dev_desc, int offset, char *buf, unsigned int nbytes); ++ int (*pwrite)(int dev_desc, int offset, char *buf, unsigned int nbytes); ++} fd_table[16]; ++ ++static int fd_index; ++static int con_pread(int dev_desc, int offset, char *buf, unsigned int nbytes); ++static int con_pwrite(int dev_desc, int offset, char *buf, unsigned int nbytes); ++ ++void * ++init_openprom_qemu(int bankc, struct bank *bankv, unsigned hiphybas, ++ const char *cmdline, char boot_device, int nographic) ++{ ++ int i; ++ ++ /* ++ * Avoid data segment allocations ++ */ ++ ptphys = totphys; ++ ptmap = totmap; ++ ptavail = totavail; ++ /* ++ * Form memory descriptors. ++ */ ++ for (i = 0; i < bankc; i++) { ++ totphys[i].theres_more = &totphys[i+1]; ++ totphys[i].start_adr = (char*) bankv[i].start; ++ totphys[i].num_bytes = bankv[i].length; ++ } ++ totphys[i-1].theres_more = 0; ++ ++ /* ++ * XXX Merged in normal PROM when full banks touch. ++ */ ++ for (i = 0; i < bankc; i++) { ++ unsigned bankbase = bankv[i].start; ++ unsigned banksize = bankv[i].length; ++ if (hiphybas > bankbase && ++ hiphybas < bankbase + banksize) { ++ banksize = hiphybas - bankbase; ++ } ++ totavail[i].theres_more = &totavail[i+1]; ++ totavail[i].start_adr = (char*) bankbase; ++ totavail[i].num_bytes = banksize; ++ } ++ totavail[i-1].theres_more = 0; ++ ++ totmap[0].theres_more = 0; ++ totmap[0].start_adr = (char*) PROLBASE; ++ totmap[0].num_bytes = PROLSIZE; ++ prop_mem_reg[0] = 0; ++ prop_mem_reg[1] = 0; ++ prop_mem_reg[2] = bankv[0].length; ++ prop_mem_avail[0] = 0; ++ prop_mem_avail[1] = 0; ++ prop_mem_avail[2] = hiphybas; ++ prop_vmem_avail[0] = 0; ++ prop_vmem_avail[1] = 0; ++ prop_vmem_avail[2] = PROLBASE-1; ++ prop_vmem_avail[3] = 0; ++ prop_vmem_avail[4] = 0xffe00000; ++ prop_vmem_avail[5] = 0x00200000; ++ ++ /* ++ * idprom ++ */ ++ bcopy(idprom, obp_idprom, IDPROM_SIZE); ++ ++ // Linux wants a R/W romvec table ++ romvec0.pv_magic_cookie = LINUX_OPPROM_MAGIC; ++ romvec0.pv_romvers = 3; ++ romvec0.pv_plugin_revision = 77; ++ romvec0.pv_printrev = 0x10203; ++ romvec0.pv_v0mem.v0_totphys = &ptphys; ++ romvec0.pv_v0mem.v0_prommap = &ptmap; ++ romvec0.pv_v0mem.v0_available = &ptavail; ++ romvec0.pv_nodeops = &nodeops0; ++ romvec0.pv_bootstr = (void *)doublewalk; ++ romvec0.pv_v0devops.v0_devopen = &obp_devopen; ++ romvec0.pv_v0devops.v0_devclose = &obp_devclose; ++ romvec0.pv_v0devops.v0_rdblkdev = &obp_rdblkdev; ++ romvec0.pv_stdin = &obp_stdin; ++ romvec0.pv_stdout = &obp_stdout; ++ romvec0.pv_getchar = obp_nbgetchar; ++ romvec0.pv_putchar = (void (*)(int))obp_nbputchar; ++ romvec0.pv_nbgetchar = obp_nbgetchar; ++ romvec0.pv_nbputchar = obp_nbputchar; ++ romvec0.pv_reboot = obp_reboot; ++ romvec0.pv_printf = (void (*)(const char *fmt, ...))printk; ++ romvec0.pv_abort = obp_abort; ++ romvec0.pv_halt = obp_halt; ++ romvec0.pv_synchook = &synch_hook; ++ romvec0.pv_v0bootargs = &obp_argp; ++ romvec0.pv_v2devops.v2_inst2pkg = obp_inst2pkg; ++ romvec0.pv_v2devops.v2_dumb_mmap = obp_dumb_mmap; ++ romvec0.pv_v2devops.v2_dumb_munmap = obp_dumb_munmap; ++ romvec0.pv_v2devops.v2_dev_open = obp_devopen; ++ romvec0.pv_v2devops.v2_dev_close = (void (*)(int))obp_devclose; ++ romvec0.pv_v2devops.v2_dev_read = obp_devread; ++ romvec0.pv_v2devops.v2_dev_write = obp_devwrite; ++ romvec0.pv_v2devops.v2_dev_seek = obp_devseek; ++ obp_arg.boot_dev_ctrl = 0; ++ obp_arg.boot_dev_unit = '0'; ++ obp_arg.argv[0] = "sd(0,0,0):d"; ++ switch(boot_device) { ++ default: ++ case 'a': ++ obp_arg.argv[0] = "fd()"; ++ obp_arg.boot_dev[0] = 'f'; ++ obp_arg.boot_dev[1] = 'd'; ++ break; ++ case 'd': ++ obp_arg.boot_dev_unit = '2'; ++ obp_arg.argv[0] = "sd(0,2,0):d"; ++ // Fall through ++ case 'c': ++ obp_arg.boot_dev[0] = 's'; ++ obp_arg.boot_dev[1] = 'd'; ++ break; ++ case 'n': ++ obp_arg.argv[0] = "le()"; ++ obp_arg.boot_dev[0] = 'l'; ++ obp_arg.boot_dev[1] = 'e'; ++ break; ++ } ++ obp_arg.argv[1] = cmdline; ++ romvec0.pv_v2bootargs.bootpath = &obp_arg.argv[0]; ++ romvec0.pv_v2bootargs.bootargs = &cmdline; ++ romvec0.pv_v2bootargs.fd_stdin = &obp_fd_stdin; ++ romvec0.pv_v2bootargs.fd_stdout = &obp_fd_stdout; ++ ++ bcopy(propv_root_templ, propv_root, sizeof(propv_root_templ)); ++ propv_root[4].name = "stdin-path"; ++ propv_root[5].name = "stdout-path"; ++ obp_fd_stdin = 0; ++ obp_fd_stdout = 1; ++ fd_table[0].pread = con_pread; ++ fd_table[0].pwrite = con_pwrite; ++ fd_table[1].pread = con_pread; ++ fd_table[1].pwrite = con_pwrite; ++ fd_index = 2; ++ if (nographic) { ++ obp_stdin = PROMDEV_TTYA; ++ propv_root[4].value = "/obio/zs@0,100000:a"; ++ propv_root[4].length = sizeof("/obio/zs@0,100000:a"); ++ fd_table[0].unit = 18; ++ obp_stdout = PROMDEV_TTYA; ++ propv_root[5].value = "/obio/zs@0,100000:a"; ++ propv_root[5].length = sizeof("/obio/zs@0,100000:a"); ++ fd_table[1].unit = 18; ++ } else { ++ obp_stdin = PROMDEV_KBD; ++ propv_root[4].value = "/obio/zs@0,0"; ++ propv_root[4].length = sizeof("/obio/zs@0,0"); ++ fd_table[0].unit = 19; ++ obp_stdout = PROMDEV_SCREEN; ++ propv_root[5].value = "/iommu/sbus/SUNW,tcx"; ++ propv_root[5].length = sizeof("/iommu/sbus/SUNW,tcx"); ++ fd_table[1].unit = 4; ++ } ++ prop_zs_addr = map_io(0x71000000, 8); ++ prop_zs1_addr = map_io(0x71100000, 8); ++ return &romvec0; ++} ++ ++static const struct property *find_property(int node,char *name) ++{ ++ const struct property *prop = &nodes[node].properties[0]; ++ while (prop && prop->name) { ++ if (bcmp(prop->name, name, 128) == 0) return prop; ++ prop++; ++ } ++ return NULL; ++} ++ ++static int obp_nextnode(int node) ++{ ++#ifdef DEBUG_OBP ++ printk("obp_nextnode(%d) = %d\n", node, nodes[node].sibling); ++#endif ++ return nodes[node].sibling; ++} ++ ++static int obp_child(int node) ++{ ++#ifdef DEBUG_OBP ++ printk("obp_child(%d) = %d\n", node, nodes[node].child); ++#endif ++ return nodes[node].child; ++} ++ ++static int obp_proplen(int node, char *name) ++{ ++ const struct property *prop = find_property(node,name); ++ if (prop) { ++#ifdef DEBUG_OBP ++ printk("obp_proplen(%d, %s) = %d\n", node, name, prop->length); ++#endif ++ return prop->length; ++ } ++#ifdef DEBUG_OBP ++ printk("obp_proplen(%d, %s) (no prop)\n", node, name); ++#endif ++ return -1; ++} ++ ++static int obp_getprop(int node, char *name, char *value) ++{ ++ const struct property *prop; ++ ++ if (!name) { ++ // NULL name means get first property ++#ifdef DEBUG_OBP ++ printk("obp_getprop(%d, %x (NULL)) = %s\n", node, name, ++ nodes[node].properties[0].name); ++#endif ++ return (int)nodes[node].properties[0].name; ++ } ++ prop = find_property(node,name); ++ if (prop) { ++ memcpy(value,prop->value,prop->length); ++#ifdef DEBUG_OBP ++ printk("obp_getprop(%d, %s) = %s\n", node, name, value); ++#endif ++ return prop->length; ++ } ++#ifdef DEBUG_OBP ++ printk("obp_getprop(%d, %s): not found\n", node, name); ++#endif ++ return -1; ++} ++ ++static int obp_setprop(__attribute__((unused)) int node, ++ __attribute__((unused)) char *name, ++ __attribute__((unused)) char *value, ++ __attribute__((unused)) int len) ++{ ++#ifdef DEBUG_OBP ++ printk("obp_setprop(%d, %s) = %s (%d)\n", node, name, value, len); ++#endif ++ return -1; ++} ++ ++static const char *obp_nextprop(int node,char *name) ++{ ++ const struct property *prop; ++ ++ if (!name || *name == '\0') { ++ // NULL name means get first property ++#ifdef DEBUG_OBP ++ printk("obp_nextprop(%d, NULL) = %s\n", node, ++ nodes[node].properties[0].name); ++#endif ++ return nodes[node].properties[0].name; ++ } ++ prop = find_property(node,name); ++ if (prop && prop[1].name) { ++#ifdef DEBUG_OBP ++ printk("obp_nextprop(%d, %s) = %s\n", node, name, prop[1].name); ++#endif ++ return prop[1].name; ++ } ++#ifdef DEBUG_OBP ++ printk("obp_nextprop(%d, %s): not found\n", node, name); ++#endif ++ return ""; ++} ++ ++extern int (*getch_fn)(struct vconterm *v); ++ ++static int obp_nbgetchar(void) { ++ extern struct vconterm dp0; ++ return getch_fn(&dp0); ++} ++ ++static int obp_nbputchar(int ch) { ++ printk("%c", ch); ++ return 0; ++} ++ ++static void obp_reboot(char *str) { ++ printk("rebooting (%s)\n", str); ++ stb_bypass(0x71f00000, 1); ++ for (;;) {} ++} ++ ++static void obp_abort() { ++ printk("abort, power off\n"); ++ stb_bypass(0x71910000, 1); ++ for (;;) {} ++} ++ ++static void obp_halt() { ++ printk("halt, power off\n"); ++ stb_bypass(0x71910000, 1); ++ for (;;) {} ++} ++ ++extern void *esp_read(int unit, int part, int offset, short len); ++ ++static int esp_pread(int dev_desc, int offset, char *buf, unsigned int nbytes) ++{ ++ unsigned int i; ++ void *src; ++ ++ for(i = 0; i < nbytes; i += 512) { ++ src = esp_read(fd_table[dev_desc].unit, fd_table[dev_desc].part, (offset + i) / 512, 512); ++ memcpy(&buf[i], src, 512); ++ } ++ return nbytes; ++} ++ ++static int con_pread(__attribute__((unused)) int dev_desc, __attribute__((unused)) int offset, char *buf, unsigned int nbytes) ++{ ++ unsigned int i; ++ ++ for(i = 0; i < nbytes; i ++) { ++ buf[i] = obp_nbgetchar(); ++ } ++ return nbytes; ++} ++ ++static int con_pwrite(__attribute__((unused)) int dev_desc, __attribute__((unused)) int offset, char *buf, unsigned int nbytes) ++{ ++ unsigned int i; ++ ++ for(i = 0; i < nbytes; i ++) { ++ obp_nbputchar(buf[i]); ++ } ++ return nbytes; ++} ++ ++#define isnum(c) ((c >= '0') && (c < '9')) ++#define ctoi(c) (c - '0') ++ ++static int obp_devopen(char *str) { ++#ifdef DEBUG_OBP ++ printk("obp_devopen(%s)\n", str); ++#endif ++ if (str[0] == 's' && str[1] == 'd' && str[4] == ',') { ++ unsigned int target; ++ ++ if (str[5] < 7) ++ target = str[5]; ++ else if (isnum(str[6]) && isnum(str[5])) { ++ target = (ctoi(str[5]) * 10 + ctoi(str[6])) & 7; ++ } ++ else { ++ target = ctoi(str[5]) & 7; ++ } ++ fd_table[fd_index].unit = target; ++ fd_table[fd_index].part = str[10] - 'a'; ++ fd_table[fd_index].pread = esp_pread; ++ return fd_index++; // XXX ++ } ++ return 0; ++} ++ ++static int obp_devclose(__attribute__((unused)) int dev_desc) { ++#ifdef DEBUG_OBP ++ printk("obp_devclose %d\n", dev_desc); ++#endif ++ fd_index--; // XXX ++ return 0; ++} ++ ++static int obp_rdblkdev(int dev_desc, int num_blks, int offset, char *buf) ++{ ++#ifdef DEBUG_OBP ++ printk("obp_rdblkdev: fd %d, num_blks %d, offset %d, buf 0x%x\n", dev_desc, num_blks, offset, buf); ++#endif ++ return fd_table[dev_desc].pread(dev_desc, offset, buf, num_blks * 512); ++} ++ ++static char *obp_dumb_mmap(char *va, __attribute__((unused)) int which_io, ++ unsigned int pa, unsigned int size) ++{ ++ unsigned int npages; ++ unsigned int off; ++ unsigned int mva; ++ ++#ifdef DEBUG_OBP ++ printk("obp_dumb_mmap: virta %x, which_io %d, paddr %x, sz %d\n", va, which_io, pa, size); ++#endif ++ off = pa & (PAGE_SIZE-1); ++ npages = (off + size + (PAGE_SIZE-1)) / PAGE_SIZE; ++ pa &= ~(PAGE_SIZE-1); ++ ++ mva = (unsigned int) va; ++ while (npages-- != 0) { ++ map_page(pmem.pl1, mva, pa, 1, pmem.pbas); ++ mva += PAGE_SIZE; ++ pa += PAGE_SIZE; ++ } ++ return va; ++} ++ ++static void obp_dumb_munmap(__attribute__((unused)) char *va, ++ __attribute__((unused)) unsigned int size) ++{ ++#ifdef DEBUG_OBP ++ printk("obp_dumb_munmap: virta %x, sz %d\n", va, size); ++#endif ++} ++ ++static int obp_devread(int dev_desc, char *buf, int nbytes) ++{ ++ int ret; ++#ifdef DEBUG_OBP ++ printk("obp_devread: fd %d, nbytes %d\n", dev_desc, nbytes); ++#endif ++ ret = fd_table[dev_desc].pread(dev_desc, fd_table[dev_desc].offset, buf, nbytes); ++ fd_table[dev_desc].offset += nbytes; ++ return ret; ++} ++ ++static int obp_devwrite(int dev_desc, char *buf, int nbytes) ++{ ++ int ret; ++#ifdef DEBUG_OBP ++ printk("obp_devwrite: fd %d, buf %s, nbytes %d\n", dev_desc, buf, nbytes); ++#endif ++ ret = fd_table[dev_desc].pwrite(dev_desc, fd_table[dev_desc].offset, buf, nbytes); ++ fd_table[dev_desc].offset += nbytes; ++ return ret; ++} ++ ++static int obp_devseek(int dev_desc, __attribute__((unused)) int hi, int lo) ++{ ++#ifdef DEBUG_OBP ++ printk("obp_devseek: fd %d, hi %d, lo %d\n", dev_desc, hi, lo); ++#endif ++ fd_table[dev_desc].offset = lo; ++ return 0; ++} ++ ++static int obp_inst2pkg(int dev_desc) ++{ ++#ifdef DEBUG_OBP ++ printk("obp_inst2pkg: fd %d\n", dev_desc); ++#endif ++ return fd_table[dev_desc].unit; ++} +diff -ruN proll_18.orig/qemu/system_qemu.c proll-patch-15/qemu/system_qemu.c +--- proll_18.orig/qemu/system_qemu.c 1970-01-01 00:00:00.000000000 +0000 ++++ proll-patch-15/qemu/system_qemu.c 2005-04-16 06:16:20.000000000 +0000 +@@ -0,0 +1,430 @@ ++/** ++ ** Proll (PROM replacement) ++ ** system.c: shared miscallenea. ++ ** Copyright 1999 Pete Zaitcev ++ ** This code is licensed under GNU General Public License. ++ **/ ++#include ++#include ++#include ++#ifndef NULL ++#define NULL ((void*)0) ++#endif ++ ++#include "pgtsrmmu.h" ++ ++#include "vconsole.h" ++#include /* Local copy of 2.2 style include */ ++#include /* __P() */ ++#include /* init_net() */ ++#include /* we are a provider for part of this. */ ++#include /* myipaddr */ ++#include ++#include /* our own prototypes */ ++ ++/* ++ * We export this. ++ */ ++char idprom[IDPROM_SIZE]; ++ ++ ++/* ++ * Create an I/O mapping to pa[size]. ++ * Returns va of the mapping or 0 if unsuccessful. ++ */ ++void * ++map_io(unsigned pa, int size) ++{ ++ void *va; ++ unsigned int npages; ++ unsigned int off; ++ unsigned int mva; ++ ++ off = pa & (PAGE_SIZE-1); ++ npages = (off + size + (PAGE_SIZE-1)) / PAGE_SIZE; ++ pa &= ~(PAGE_SIZE-1); ++ ++ va = mem_alloc(&cio, npages*PAGE_SIZE, PAGE_SIZE); ++ if (va == 0) return va; ++ ++ mva = (unsigned int) va; ++ /* printk("map_io: va 0x%x pa 0x%x off 0x%x npages %d\n", va, pa, off, npages); */ /* P3 */ ++ while (npages-- != 0) { ++ map_page(pmem.pl1, mva, pa, 1, pmem.pbas); ++ mva += PAGE_SIZE; ++ pa += PAGE_SIZE; ++ } ++ ++ return (void *)((unsigned int)va + off); ++} ++ ++/* ++ * Tablewalk routine used for testing. ++ * Returns PTP/PTE. ++ */ ++unsigned int ++proc_tablewalk(int ctx, unsigned int va) ++{ ++ unsigned int pa1; ++ ++ __asm__ __volatile__ ("lda [%1] %2, %0" : ++ "=r" (pa1) : ++ "r" (AC_M_CTPR), "i" (ASI_M_MMUREGS)); ++ /* printk(" ctpr %x ctx %x\n", pa1, ctx); */ /* P3 */ ++ pa1 <<= 4; ++ pa1 = ld_bypass(pa1 + (ctx << 2)); ++ if ((pa1 & 0x03) == 0) goto invalid; ++ return mem_tablewalk((pa1 & 0xFFFFFFF0) << 4, va); ++ ++invalid: ++ printk(" invalid %x\n", pa1); ++ return 0; ++} ++ ++/* ++ * Walk the tables in memory, starting at physical address pa. ++ */ ++unsigned int ++mem_tablewalk(unsigned int pa, unsigned int va) ++{ ++ unsigned int pa1; ++ ++ printk("pa %x va %x", pa, va); ++ pa1 = ld_bypass(pa + (((va&0xFF000000)>>24) << 2)); ++ if ((pa1 & 0x03) == 0) goto invalid; ++ printk(" l1 %x", pa1); ++ pa1 <<= 4; pa1 &= 0xFFFFFF00; ++ pa1 = ld_bypass(pa1 + (((va&0x00FC0000)>>18) << 2)); ++ if ((pa1 & 0x03) == 0) goto invalid; ++ printk(" l2 %x", pa1); ++ pa1 <<= 4; pa1 &= 0xFFFFFF00; ++ pa1 = ld_bypass(pa1 + (((va&0x0003F000)>>12) << 2)); ++ if ((pa1 & 0x03) == 0) goto invalid; ++ printk(" l3 %x", pa1); ++ printk(" off %x\n", va&0x00000FFF); ++ return pa1; ++invalid: ++ printk(" invalid %x\n", pa1); ++ return 0; ++} ++ ++/* ++ * Make CPU page tables. ++ * Returns pointer to context table. ++ * Here we ignore memory allocation errors which "should not happen" ++ * because we cannot print anything anyways if memory initialization fails. ++ */ ++void makepages(struct phym *t, unsigned int highbase) ++{ ++ unsigned int *ctp, *l1, pte; ++ int i; ++ unsigned int pa, va; ++ ++ ctp = mem_zalloc(&cmem, NCTX_SWIFT*sizeof(int), NCTX_SWIFT*sizeof(int)); ++ l1 = mem_zalloc(&cmem, 256*sizeof(int), 256*sizeof(int)); ++ ++ pte = SRMMU_ET_PTD | (((unsigned int)l1 - PROLBASE + highbase) >> 4); ++ for (i = 0; i < NCTX_SWIFT; i++) { ++ ctp[i] = pte; ++ } ++ ++ pa = PROLBASE; ++ for (va = PROLBASE; va < PROLDATA; va += PAGE_SIZE) { ++ map_page(l1, va, pa, 0, highbase); ++ pa += PAGE_SIZE; ++ } ++ pa = highbase + PROLDATA - PROLBASE; ++ for (va = PROLDATA; va < PROLBASE + PROLSIZE; va += PAGE_SIZE) { ++ map_page(l1, va, pa, 0, highbase); ++ pa += PAGE_SIZE; ++ } ++ ++ /* We need to start from LOADBASE, but kernel wants PAGE_SIZE. */ ++ pa = 0; ++ for (va = 0; va < LOWMEMSZ; va += PAGE_SIZE) { ++ map_page(l1, va, pa, 0, highbase); ++ pa += PAGE_SIZE; ++ } ++ ++ t->pctp = ctp; ++ t->pl1 = l1; ++ t->pbas = highbase; ++} ++ ++/* ++ * Create a memory mapping from va to epa in page table pgd. ++ * highbase is used for v2p translation. ++ */ ++int ++map_page(unsigned int *pgd, unsigned int va, ++ unsigned int epa, int type, unsigned int highbase) ++{ ++ unsigned int pte; ++ unsigned int *p; ++ unsigned int pa; ++ ++ pte = pgd[((va)>>SRMMU_PGDIR_SHIFT) & (SRMMU_PTRS_PER_PGD-1)]; ++ if ((pte & SRMMU_ET_MASK) == SRMMU_ET_INVALID) { ++ p = mem_zalloc(&cmem, SRMMU_PTRS_PER_PMD*sizeof(int), ++ SRMMU_PTRS_PER_PMD*sizeof(int)); ++ if (p == 0) goto drop; ++ pte = SRMMU_ET_PTD | ++ (((unsigned int)p - PROLBASE + highbase) >> 4); ++ pgd[((va)>>SRMMU_PGDIR_SHIFT) & (SRMMU_PTRS_PER_PGD-1)] = pte; ++ /* barrier() */ ++ } ++ ++ pa = ((pte & 0xFFFFFFF0) << 4); ++ pa += (((va)>>SRMMU_PMD_SHIFT & (SRMMU_PTRS_PER_PMD-1)) << 2); ++ pte = ld_bypass(pa); ++ if ((pte & SRMMU_ET_MASK) == SRMMU_ET_INVALID) { ++ p = mem_zalloc(&cmem, SRMMU_PTRS_PER_PTE*sizeof(int), ++ SRMMU_PTRS_PER_PTE*sizeof(int)); ++ if (p == 0) goto drop; ++ pte = SRMMU_ET_PTD | ++ (((unsigned int)p - PROLBASE + highbase) >> 4); ++ st_bypass(pa, pte); ++ } ++ ++ pa = ((pte & 0xFFFFFFF0) << 4); ++ pa += (((va)>>PAGE_SHIFT & (SRMMU_PTRS_PER_PTE-1)) << 2); ++ ++ pte = SRMMU_ET_PTE | ((epa & PAGE_MASK) >> 4); ++ if (type) { /* I/O */ ++ pte |= SRMMU_REF; ++ /* SRMMU cannot make Supervisor-only, but not exectutable */ ++ pte |= SRMMU_PRIV; ++ } else { /* memory */ ++ pte |= SRMMU_REF|SRMMU_CACHE; ++ pte |= SRMMU_PRIV; /* Supervisor only access */ ++ } ++ st_bypass(pa, pte); ++ return 0; ++ ++drop: ++ return -1; ++} ++ ++/* ++ * Switch page tables. ++ */ ++void ++init_mmu_swift(unsigned int ctp_phy) ++{ ++ unsigned int addr; ++ ++ /* ++ * Flush cache ++ */ ++ for (addr = 0; addr < 0x2000; addr += 0x10) { ++ __asm__ __volatile__ ("sta %%g0, [%0] %1\n\t" : : ++ "r" (addr), "i" (ASI_M_DATAC_TAG)); ++ __asm__ __volatile__ ("sta %%g0, [%0] %1\n\t" : : ++ "r" (addr<<1), "i" (ASI_M_TXTC_TAG)); ++ } ++ ++ /* ++ * Switch ctx table ++ */ ++ ctp_phy >>= 4; ++ /* printk("done flushing, switching to %x\n", ctp_phy); */ ++ __asm__ __volatile__ ("sta %0, [%1] %2\n\t" : : ++ "r" (ctp_phy), "r" (AC_M_CTPR), "i" (ASI_M_MMUREGS)); ++ ++ /* ++ * Flush old page table references ++ */ ++ __asm__ __volatile__ ("sta %%g0, [%0] %1\n\t" : : ++ "r" (0x400), "i" (ASI_M_FLUSH_PROBE) : "memory"); ++} ++ ++/* ++ * add_timer, del_timer ++ * This should go into sched.c, but we have it split for different archs. ++ */ ++struct timer_list_head { ++ struct timer_list *head, *tail; ++}; ++ ++static struct timer_list_head timers; /* Anonymous heap of timers */ ++ ++void add_timer(struct timer_list *timer) { ++ struct timer_list *p; ++ if (timer->prev != NULL || timer->next != NULL) { ++ printk("bug: kernel timer added twice at 0x%x.\n", ++ __builtin_return_address(0)); ++ return; ++ } ++ if ((p = timers.tail) != NULL) { ++ timer->prev = p; ++ p->next = timer; ++ timers.tail = timer; ++ } else { ++ timers.head = timer; ++ timers.tail = timer; ++ } ++ return; ++} ++ ++int del_timer(struct timer_list *timer) { ++ struct timer_list *p; ++ int ret; ++ ++ if (timers.head == timer) timers.head = timer->next; ++ if (timers.tail == timer) timers.tail = timer->prev; ++ if ((p = timer->prev) != NULL) p->next = timer->next; ++ if ((p = timer->next) != NULL) p->prev = timer->prev; ++ ret = timer->next != 0 || timer->prev != 0; ++ timer->next = NULL; ++ timer->prev = NULL; ++ return ret; ++} ++ ++void run_timers() { ++ struct timer_list *p; ++ ++ p = timers.head; ++ while (p != NULL) { ++ if (p->expires < jiffies) { ++ del_timer(p); /* XXX make nonstatic member */ ++ (*p->function)(p->data); ++ p = timers.head; ++ } else { ++ p = p->next; ++ } ++ } ++} ++ ++/* ++ * Allocate memory. This is reusable. ++ */ ++void mem_init(struct mem *t, char *begin, char *limit) ++{ ++ t->start = begin; ++ t->uplim = limit; ++ t->curp = begin; ++} ++ ++void mem_fini(struct mem *t) ++{ ++ t->curp = 0; ++} ++ ++void *mem_alloc(struct mem *t, int size, int align) ++{ ++ char *p; ++ ++ p = (char *)((((unsigned int)t->curp) + (align-1)) & ~(align-1)); ++ if (p >= t->uplim || p + size > t->uplim) return 0; ++ t->curp = p + size; ++ return p; ++} ++ ++void *mem_zalloc(struct mem *t, int size, int align) ++{ ++ char *p; ++ ++ if ((p = mem_alloc(t, size, align)) != 0) memset(p, 0, size); ++ return p; ++} ++ ++/* ++ * Library functions ++ */ ++void *memset(void *s, int c, size_t len) ++{ ++ void *p = s; ++ ++ while (len--) { ++ *(char *)s = c; ++ s++; ++ } ++ return p; ++} ++ ++void bcopy(const void *f, void *t, int len) { ++ while (len--) { ++ *(char *)t = *(char *)f; ++ f++; ++ t++; ++ } ++} ++ ++/* Comparison is 7-bit */ ++int bcmp(const void *s1, const void *s2, int len) ++{ ++ int i; ++ char ch; ++ ++ while (len--) { ++ ch = *(char *)s1; ++ i = ch - *(char *)s2; ++ s1++; ++ s2++; ++ if (i != 0) ++ return i; ++ if (ch == 0) ++ return 0; ++ } ++ return 0; ++} ++ ++int strlen(const char *s) { ++ const char *p; ++ for (p = s; *p != 0; p++) { } ++ return p - s; ++} ++ ++extern void *printk_fn; ++ ++void printk(char *fmt, ...) ++{ ++ struct prf_fp { ++ void *xfp; ++ void (*write)(void *, char *, int); ++ } prfa; ++ extern void prf(struct prf_fp *, char *fmt, va_list adx); ++ va_list x1; ++ ++ va_start(x1, fmt); ++ prfa.xfp = &dp0; ++ prfa.write = printk_fn; ++ prf(&prfa, fmt, x1); ++ va_end(x1); ++} ++ ++void fatal() ++{ ++ printk("fatal."); ++loop: goto loop; ++} ++ ++/* ++ * Get the highest bit number from the mask. ++ */ ++int highc(int mask, int size) ++{ ++ int m1; ++ ++ m1 = 1 << size; ++ while (size != 0) { ++ size--; ++ m1 >>= 1; ++ if (m1 & mask) break; ++ } ++ return size; ++} ++ ++/* ++ */ ++unsigned int ld_bp_swap(unsigned int ptr) { ++ unsigned int n; ++ n = ld_bypass(ptr); ++ n = (n>>24 & 0xFF) | (n>>8 & 0xFF00) | ((n&0xFF00) << 8) | (n<<24); ++ return n; ++} ++ ++void st_bp_swap(unsigned int ptr, unsigned int n) { ++ n = (n>>24 & 0xFF) | (n>>8 & 0xFF00) | ((n&0xFF00) << 8) | (n<<24); ++ st_bypass(ptr, n); ++}; +diff -ruN proll_18.orig/src/arp.c proll-patch-15/src/arp.c +--- proll_18.orig/src/arp.c 2001-12-24 05:12:31.000000000 +0000 ++++ proll-patch-15/src/arp.c 2005-08-14 10:10:11.000000000 +0000 +@@ -45,7 +45,7 @@ + #endif + static struct arp_cache arp_list[ARPNUM]; /* ARP address cache */ + static int next_arp; /* next table entry */ +-static t_ipaddr def_gw = IP_ANY; /* default routing */ ++static t_ipaddr def_gw; /* default routing */ + + + +@@ -100,10 +100,7 @@ + * + * ARP receiver routine + */ +-static int arp_recv(buf, bufsize, addr) +-unsigned char *buf; +-int bufsize; +-unsigned char *addr; ++static int arp_recv(unsigned char *buf, unsigned int bufsize, unsigned char *addr) + { + register struct arphdr *ahp = (struct arphdr *)buf; + +@@ -144,7 +141,7 @@ + * + * Resolve IP address and return pointer to hardware address. + */ +-unsigned char *ip_resolve(ip) ++const unsigned char *ip_resolve(ip) + t_ipaddr ip; + { + int i; +@@ -230,14 +227,11 @@ + */ + int init_arp() + { +- /* Set name of module for error messages */ +- net_module_name = "arp"; +- + #ifndef NOARP + /* Register ARP packet type and set send buffer pointer */ + if ((arpbuf = (struct arphdr *)reg_type(htons(ETH_P_ARP), arp_recv)) == NULL) + return(FALSE); + #endif +- ++ def_gw = IP_ANY; + return(TRUE); + } +diff -ruN proll_18.orig/src/arp.h proll-patch-15/src/arp.h +--- proll_18.orig/src/arp.h 1999-03-18 03:39:43.000000000 +0000 ++++ proll-patch-15/src/arp.h 2004-11-13 15:50:49.000000000 +0000 +@@ -104,7 +104,7 @@ + extern int init_arp __P((void)); + + /* Resolve IP address and return pointer to hardware address */ +-extern unsigned char *ip_resolve __P((t_ipaddr ip)); ++extern const unsigned char *ip_resolve __P((t_ipaddr ip)); + + /* Add a new antry to the ARP cache */ + extern void addcache __P((unsigned char *ha, t_ipaddr ip)); +diff -ruN proll_18.orig/src/bootp.c proll-patch-15/src/bootp.c +--- proll_18.orig/src/bootp.c 1999-12-15 17:20:30.000000000 +0000 ++++ proll-patch-15/src/bootp.c 2005-08-14 10:16:09.000000000 +0000 +@@ -151,7 +151,7 @@ + while (TRUE) { + boot_xid = get_ticks() + random(); + bootp_send(); +- i = udp_read((char *)(&boot_rec), BOOTP_REC_SIZE, timeout, CHR_ESC); ++ i = udp_read((char *)(&boot_rec), BOOTP_REC_SIZE, timeout); + if (i < 0) { /* user pressed ESC */ + printf("\nAborted\n"); + return(1); +diff -ruN proll_18.orig/src/esp.c proll-patch-15/src/esp.c +--- proll_18.orig/src/esp.c 1970-01-01 00:00:00.000000000 +0000 ++++ proll-patch-15/src/esp.c 2005-08-15 18:42:46.000000000 +0000 +@@ -0,0 +1,305 @@ ++#include /* == */ ++#include /* __P for netpriv.h */ ++#include /* dmaga */ ++#include ++ ++#define PHYS_JJ_ESPDMA 0x78400000 /* ESP DMA controller */ ++#define PHYS_JJ_ESP 0x78800000 /* ESP SCSI */ ++#define PHYS_JJ_ESP_IRQ 4 ++#define BUFSIZE 4096 ++/* ++ * XXX Crude ++ */ ++struct esp_dma { ++ struct sparc_dma_registers *regs; ++ enum dvma_rev revision; ++}; ++ ++struct esp_regs { ++ unsigned int regs[16]; ++}; ++ ++struct esp_private { ++ int active; /* initialized */ ++ int inst; /* iface number */ ++ ++ volatile struct esp_regs *ll; ++ __u32 buffer_dvma; ++ unsigned int irq; /* device IRQ number */ ++ int interrupt; ++ ++ struct esp_dma *espdma; /* If set this points to espdma */ ++ ++ unsigned char *buffer; ++ struct disk_info { ++ unsigned int hw_sector; ++ unsigned int part_offset[8]; ++ } disk[8]; ++}; ++ ++static void esp_interrupt(void *dev_id) ++{ ++ struct esp_private *lp = (struct esp_private *)dev_id; ++ ++ lp->interrupt = 1; ++ /* Acknowledge all the interrupt sources ASAP */ ++ ++ lp->interrupt = 0; ++} ++ ++static int esp_open (void *dev) ++{ ++ struct esp_private *lp = (struct esp_private *)dev; ++ int status = 0; ++ ++ if (request_irq(lp->irq, &esp_interrupt, (void *)dev)) { ++ printk ("Esp: Can't get irq %d\n", lp->irq); ++ return -1; ++ } ++ ++ /* On the 4m, setup the espdma to provide the upper bits for buffers */ ++ if (lp->espdma) ++ lp->espdma->regs->dma_test = ((__u32) lp->buffer_dvma) & 0xff000000; ++ ++ return status; ++} ++ ++static int esp_close (void *dev) ++{ ++ struct esp_private *lp = (struct esp_private *)dev; ++ ++ free_irq (lp->irq, (void *) dev); ++ return 0; ++} ++ ++static int ++esp_init(struct esp_private *esp, struct esp_dma *espdma, int irq) ++{ ++ volatile struct esp_regs *ll; ++ ++ /* Get the IO region */ ++ ll = map_io(PHYS_JJ_ESP, sizeof (struct esp_regs)); ++ if (ll == 0) return -1; ++ ++ esp->buffer = dvma_alloc(BUFSIZE, &esp->buffer_dvma); ++ esp->ll = ll; ++ esp->espdma = espdma; ++ esp->irq = irq; ++ ++ // Chip reset ++ stb_bypass((int)ll + 3*2, 2); ++ return 0; ++} ++ ++static int espdma_init(struct esp_dma *espdma) ++{ ++ void *p; ++ ++ /* Hardcode everything for MrCoffee. */ ++ if ((p = map_io(PHYS_JJ_ESPDMA, 0x10)) == 0) { ++ printk("espdma_init: cannot map registers\n"); ++ return -1; ++ } ++ espdma->regs = p; ++ ++ printk("dma1: "); ++ ++ switch((espdma->regs->cond_reg)&DMA_DEVICE_ID) { ++ case DMA_VERS0: ++ espdma->revision=dvmarev0; ++ printk("Revision 0 "); ++ break; ++ case DMA_ESCV1: ++ espdma->revision=dvmaesc1; ++ printk("ESC Revision 1 "); ++ break; ++ case DMA_VERS1: ++ espdma->revision=dvmarev1; ++ printk("Revision 1 "); ++ break; ++ case DMA_VERS2: ++ espdma->revision=dvmarev2; ++ printk("Revision 2 "); ++ break; ++ case DMA_VERHME: ++ espdma->revision=dvmahme; ++ printk("HME DVMA gate array "); ++ break; ++ case DMA_VERSPLUS: ++ espdma->revision=dvmarevplus; ++ printk("Revision 1 PLUS "); ++ break; ++ default: ++ printk("unknown dma version %x", ++ (espdma->regs->cond_reg)&DMA_DEVICE_ID); ++ /* espdma->allocated = 1; */ ++ break; ++ } ++ printk("\n"); ++ return 0; ++} ++ ++static struct esp_dma espdma0; ++static struct esp_private esp; ++/* ++ * Find all the esp cards on the system and initialize them ++ */ ++void esp_probe () ++{ ++ if (espdma_init(&espdma0) != 0) { ++ return; ++ } ++ ++ if (esp_init(&esp, &espdma0, PHYS_JJ_ESP_IRQ) != 0) { ++ printk("esp_probe: esp0 init failed\n"); ++ return; ++ } ++ return; ++} ++ ++void esp_read_capacity(int unit) ++{ ++ // Set SCSI target ++ stb_bypass(PHYS_JJ_ESP + 4*4, unit & 7); ++ // Set DMA address ++ st_bypass(PHYS_JJ_ESPDMA + 4, esp.buffer_dvma); ++ // Set DMA length ++ stb_bypass(PHYS_JJ_ESP + 0*4, 10); ++ stb_bypass(PHYS_JJ_ESP + 1*4, 0); ++ // Set DMA direction ++ st_bypass(PHYS_JJ_ESPDMA + 0, 0x000); ++ // Setup command = Read Capacity ++ esp.buffer[0] = 0x80; ++ esp.buffer[1] = 0x25; ++ esp.buffer[2] = 0x00; ++ esp.buffer[3] = 0x00; ++ esp.buffer[4] = 0x00; ++ esp.buffer[5] = 0x00; ++ esp.buffer[6] = 0x00; ++ esp.buffer[7] = 0x00; ++ esp.buffer[8] = 0x00; ++ esp.buffer[9] = 0x00; ++ esp.buffer[10] = 0x00; ++ // Set ATN, issue command ++ stb_bypass(PHYS_JJ_ESP + 3*4, 0xc2); ++ ++ // Set DMA length = 512 * read length ++ stb_bypass(PHYS_JJ_ESP + 0*4, 0); ++ stb_bypass(PHYS_JJ_ESP + 1*4, 8 & 0xff); ++ // Set DMA direction ++ st_bypass(PHYS_JJ_ESPDMA + 0, 0x100); ++ // Transfer ++ stb_bypass(PHYS_JJ_ESP + 3*4, 0x90); ++ esp.disk[unit].hw_sector = (esp.buffer[4] << 24) | (esp.buffer[5] << 16) | (esp.buffer[6] << 8) | esp.buffer[7]; ++} ++ ++// offset is multiple of 512, len in bytes ++void *esp_read(int unit, int part, int offset, short len) ++{ ++ int pos, hw_sect, sect_offset, spb; ++ ++ // Set SCSI target ++ stb_bypass(PHYS_JJ_ESP + 4*4, unit & 7); ++ // Set DMA address ++ st_bypass(PHYS_JJ_ESPDMA + 4, esp.buffer_dvma); ++ // Set DMA length ++ stb_bypass(PHYS_JJ_ESP + 0*4, 10); ++ stb_bypass(PHYS_JJ_ESP + 1*4, 0); ++ // Set DMA direction ++ st_bypass(PHYS_JJ_ESPDMA + 0, 0x000); ++ hw_sect = esp.disk[unit].hw_sector; ++ offset += esp.disk[unit].part_offset[part]; ++ spb = hw_sect / 512; ++ sect_offset = offset / spb; ++ pos = (offset - sect_offset * spb) * 512; ++ len /= 512; ++ //printk("Read unit %d, offset %d -> offset %d, pos %d, hw_sect %d\n", unit, offset, sect_offset, pos, hw_sect); ++ // Setup command = Read(10) ++ esp.buffer[0] = 0x80; ++ esp.buffer[1] = 0x28; ++ esp.buffer[2] = 0x00; ++ esp.buffer[3] = (sect_offset >> 24) & 0xff; ++ esp.buffer[4] = (sect_offset >> 16) & 0xff; ++ esp.buffer[5] = (sect_offset >> 8) & 0xff; ++ esp.buffer[6] = sect_offset & 0xff; ++ esp.buffer[7] = 0x00; ++ esp.buffer[8] = (len >> 8) & 0xff; ++ esp.buffer[9] = len & 0xff; ++ // Set ATN, issue command ++ stb_bypass(PHYS_JJ_ESP + 3*4, 0xc2); ++ ++ // Set DMA length = sector size * read length ++ stb_bypass(PHYS_JJ_ESP + 0*4, (len * hw_sect) & 0xff); ++ stb_bypass(PHYS_JJ_ESP + 1*4, ((len * hw_sect) >> 8) & 0xff); ++ // Set DMA direction ++ st_bypass(PHYS_JJ_ESPDMA + 0, 0x100); ++ // Transfer ++ stb_bypass(PHYS_JJ_ESP + 3*4, 0x90); ++ return esp.buffer + pos; ++} ++ ++// Sparc boot sequence can be found in SILO docs, ++// first-isofs/README.SILO_ISOFS ++int esp_boot(int unit) ++{ ++ struct sun_disklabel { ++ unsigned char info[128]; /* Informative text string */ ++ unsigned char spare0[14]; ++ struct sun_info { ++ unsigned char spare1; ++ unsigned char id; ++ unsigned char spare2; ++ unsigned char flags; ++ } infos[8]; ++ unsigned char spare[246]; /* Boot information etc. */ ++ short rspeed; /* Disk rotational speed */ ++ short pcylcount; /* Physical cylinder count */ ++ short sparecyl; /* extra sects per cylinder */ ++ unsigned char spare2[4]; /* More magic... */ ++ short ilfact; /* Interleave factor */ ++ short ncyl; /* Data cylinder count */ ++ short nacyl; /* Alt. cylinder count */ ++ short ntrks; /* Tracks per cylinder */ ++ short nsect; /* Sectors per track */ ++ unsigned char spare3[4]; /* Even more magic... */ ++ struct sun_partition { ++ int start_cylinder; ++ int num_sectors; ++ } partitions[8]; ++ short magic; /* Magic number */ ++ short csum; /* Label xor'd checksum */ ++ } *label; ++ unsigned int i, offset; ++ void *src, *dst; ++ ++ printk("Loading partition table from target %d:\n", unit); ++ // Chip reset ++ stb_bypass(PHYS_JJ_ESP + 3*4, 2); ++ ++ esp_open(&esp); ++ esp_read_capacity(unit); ++ ++ label = esp_read(unit, 0, 0, 512); ++ printk("hw sector: %d, CHS: %d/%d/%d, partitions:\n", esp.disk[unit].hw_sector, ++ label->ncyl, label->ntrks, label->nsect); ++ for (i = 0; i < 8; i++) { ++ printk("%c: %d + %d, id %x, flags %x\n", 'a' + i, label->partitions[i].start_cylinder, ++ label->partitions[i].num_sectors, label->infos[i].id, label->infos[i].flags); ++ esp.disk[unit].part_offset[i] = label->partitions[3].start_cylinder * label->ntrks * label->nsect; ++ } ++ offset = 1; ++ printk("booting sd(0,%d,0):d (offset %d)\n", unit, offset); ++ // Skip a.out header (0x20) ++ dst = (void *)0x4000; ++ src = esp_read(unit, 3, offset, 512); ++ src = (void *)((unsigned int) src + 0x20); ++ memcpy(dst, src, 512 - 0x20); ++ dst = (void *)0x4000 + 512 - 0x20; ++ for (i = 1; i < 7680/512; i++) { ++ src = esp_read(unit, 3, offset + i, 512); ++ memcpy(dst, src, 512); ++ dst += 512; ++ } ++ esp_close(&esp); ++ return 0; ++} +diff -ruN proll_18.orig/src/hconsole.c proll-patch-15/src/hconsole.c +--- proll_18.orig/src/hconsole.c 2002-07-23 05:52:48.000000000 +0000 ++++ proll-patch-15/src/hconsole.c 2005-11-09 18:46:34.000000000 +0000 +@@ -29,6 +29,10 @@ + struct raster r_master; /* For a case of resize, whole fb */ + struct raster r_0; /* malloc() erzatz */ + ++#ifdef QEMU ++extern unsigned int q_height, q_width; ++#endif ++ + int hcon_init(struct hconsole *t, unsigned int a0) + { + struct raster *q, *r; +@@ -42,7 +46,11 @@ + * No probing sequence or argument passing, hardcode everything. XXX + */ + raster8_cons_a(q, 768, 1024, (char *)a0); ++#ifndef QEMU + raster_cons_2(r, q, 768-(24*11)-1, 1024-(8*80)-1, (24*11), (8*80)); ++#else ++ raster_cons_2(r, q, 0, 0, q_height, q_width); ++#endif + t->r_ = r; + t->r0_ = q; + t->f_ = &f_master; +@@ -67,7 +75,7 @@ + return 0; + } + +-void hcon_fini (struct hconsole *t) ++void hcon_fini (__attribute((unused)) struct hconsole *t) + { + return; + } +@@ -77,12 +85,12 @@ + { + struct rfont *f = t->f_; + +- if (sy < 0 || sy >= t->ydim_) return -1; +- if (sx < 0 || sx >= t->xdim_) return -1; ++ if (sy < 0 || (unsigned)sy >= t->ydim_) return -1; ++ if (sx < 0 || (unsigned)sx >= t->xdim_) return -1; + if (height < 0) return -1; +- if (sy + height > t->ydim_) height = t->ydim_ - sy; ++ if ((unsigned)sy + (unsigned)height > t->ydim_) height = t->ydim_ - sy; + if (width < 0) return -1; +- if (sx + width > t->xdim_) width = t->xdim_ - sx; ++ if ((unsigned)sx + (unsigned)width > t->xdim_) width = t->xdim_ - sx; + + /* XXX Clear with correct background color */ + (*t->r_->clear_)(t->r_, +@@ -107,10 +115,10 @@ + char c0 = c; + RC_color rfg, rbg; + +- if (y < 0 || y >= t->ydim_) return -1; +- if (x < 0 || x >= t->xdim_) return -1; ++ if (y < 0 || (unsigned)y >= t->ydim_) return -1; ++ if (x < 0 || (unsigned)x >= t->xdim_) return -1; + +- if (t->curson_ && t->ypos_ == y && t->xpos_ == x) { ++ if (t->curson_ && t->ypos_ == (unsigned)y && t->xpos_ == (unsigned)x) { + rfg = t->bg_; rbg = t->fg_; + } else { + rfg = t->fg_; rbg = t->bg_; +@@ -126,9 +134,9 @@ + { + struct rfont *f = t->f_; + +- if (y < 0 || y >= t->ydim_) return -1; +- if (x < 0 || x >= t->xdim_) return -1; +- if (x + count >= t->xdim_) count = t->xdim_ - x; ++ if (y < 0 || (unsigned)y >= t->ydim_) return -1; ++ if (x < 0 || (unsigned)x >= t->xdim_) return -1; ++ if ((unsigned)x + (unsigned)count >= t->xdim_) count = t->xdim_ - x; + + (*t->r_->render_)(t->r_, y*f->height_, x*f->width_, + s, count, t->bg_, t->fg_, f); +@@ -200,8 +208,8 @@ + + rc = 0; + if (dir == SM_UP) { +- if (d < 0 || d >= t->ydim_) return -1; +- if (b <= d || b > t->ydim_) return -1; ++ if (d < 0 || (unsigned)d >= t->ydim_) return -1; ++ if (b <= d || (unsigned)b > t->ydim_) return -1; + if (d + count >= b) count = b - d; + if (d + count >= b) count = b - d; + (*t->r_->yscroll_)(t->r_, +@@ -213,8 +221,8 @@ + count*f->height_, raster_qwidth(t->r_), + t->bg_); + } else if (dir == SM_DOWN) { +- if (d < 0 || d >= t->ydim_) return -1; +- if (b <= d || b > t->ydim_) return -1; ++ if (d < 0 || (unsigned)d >= t->ydim_) return -1; ++ if (b <= d || (unsigned)b > t->ydim_) return -1; + if (d + count >= b) count = b - d; + (*t->r_->yscroll_)(t->r_, + d*f->height_, 0, +diff -ruN proll_18.orig/src/hme.c proll-patch-15/src/hme.c +--- proll_18.orig/src/hme.c 2002-07-23 05:52:52.000000000 +0000 ++++ proll-patch-15/src/hme.c 2005-04-16 06:16:20.000000000 +0000 +@@ -655,10 +655,10 @@ + unsigned int flags, + unsigned int addr) + { +- __asm__ __volatile__(" +- stwa %3, [%0] %2 +- stwa %4, [%1] %2 +-" : /* no outputs */ ++ __asm__ __volatile__( ++ "stwa %3, [%0] %2\n\t" ++ "stwa %4, [%1] %2\n\t" ++ : /* no outputs */ + : "r" (&rp->rx_addr), "r" (&rp->rx_flags), + "i" (ASI_PL), "r" (addr), "r" (flags)); + } +@@ -667,10 +667,10 @@ + unsigned int flags, + unsigned int addr) + { +- __asm__ __volatile__(" +- stwa %3, [%0] %2 +- stwa %4, [%1] %2 +-" : /* no outputs */ ++ __asm__ __volatile__( ++ "stwa %3, [%0] %2\n\t" ++ "stwa %4, [%1] %2\n\t" ++ : /* no outputs */ + : "r" (&tp->tx_addr), "r" (&tp->tx_flags), + "i" (ASI_PL), "r" (addr), "r" (flags)); + } +@@ -2404,7 +2404,7 @@ + TXD(("[%d]", elem)); + this = &txbase[elem]; + #ifdef __sparc_v9__ +- __asm__ __volatile__("lduwa [%1] %2, %0" ++ __asm__ __volatile__("lduwa [%1] %2, %0\n\t" + : "=r" (flags) + : "r" (&this->tx_flags), "i" (ASI_PL)); + #else +@@ -2447,7 +2447,7 @@ + RXD(("RX<")); + this = &rxbase[elem]; + #ifdef __sparc_v9__ +- __asm__ __volatile__("lduwa [%1] %2, %0" ++ __asm__ __volatile__("lduwa [%1] %2, %0\n\t" + : "=r" (flags) + : "r" (&this->rx_flags), "i" (ASI_PL)); + #else +@@ -2530,7 +2530,7 @@ + elem = NEXT_RX(elem); + this = &rxbase[elem]; + #ifdef __sparc_v9__ +- __asm__ __volatile__("lduwa [%1] %2, %0" ++ __asm__ __volatile__("lduwa [%1] %2, %0\n\t" + : "=r" (flags) + : "r" (&this->rx_flags), "i" (ASI_PL)); + #else +diff -ruN proll_18.orig/src/iommu.c proll-patch-15/src/iommu.c +--- proll_18.orig/src/iommu.c 2002-07-23 05:52:49.000000000 +0000 ++++ proll-patch-15/src/iommu.c 2005-08-14 10:08:17.000000000 +0000 +@@ -36,7 +36,7 @@ + unsigned int pa, ba; + unsigned int npages; + unsigned int mva, mpa; +- int i; ++ unsigned int i; + unsigned int *iopte; + + npages = (size + (PAGE_SIZE-1)) / PAGE_SIZE; +diff -ruN proll_18.orig/src/lat7_2.bm proll-patch-15/src/lat7_2.bm +--- proll_18.orig/src/lat7_2.bm 1999-02-27 05:48:54.000000000 +0000 ++++ proll-patch-15/src/lat7_2.bm 2004-11-13 15:50:49.000000000 +0000 +@@ -1,6 +1,6 @@ + #define lat7_2_width 128 + #define lat7_2_height 88 +-static unsigned char lat7_2_bits[] = { ++static unsigned const char lat7_2_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x12, 0x1e, 0x0c, 0x02, 0x70, 0x18, + 0x22, 0x22, 0x18, 0x00, 0x00, 0x18, 0x18, 0xff, 0x18, 0x00, 0x12, 0x02, +diff -ruN proll_18.orig/src/lat7_2_swapped.bm proll-patch-15/src/lat7_2_swapped.bm +--- proll_18.orig/src/lat7_2_swapped.bm 1970-01-01 00:00:00.000000000 +0000 ++++ proll-patch-15/src/lat7_2_swapped.bm 2004-11-13 15:50:49.000000000 +0000 +@@ -0,0 +1,121 @@ ++#define lat7_2_width 128 ++#define lat7_2_height 88 ++static unsigned const char lat7_2_bits[] = { ++ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x55, 0x00, 0x2a, 0x00, 0x55, 0x00, 0x2a, 0x00, 0x55, 0x00, 0x00, 0x48, ++ 0x48, 0x78, 0x48, 0x5f, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x78, 0x40, ++ 0x70, 0x40, 0x4f, 0x08, 0x0e, 0x08, 0x08, 0x00, 0x00, 0x30, 0x40, 0x40, ++ 0x40, 0x3e, 0x09, 0x0e, 0x0a, 0x09, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, ++ 0x7f, 0x08, 0x0e, 0x08, 0x08, 0x00, 0x00, 0x0e, 0x0a, 0x0e, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, ++ 0x7e, 0x00, 0x00, 0x00, 0x00, 0x44, 0x64, 0x54, 0x4c, 0x54, 0x10, 0x10, ++ 0x10, 0x1f, 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x1f, 0x04, 0x04, 0x04, ++ 0x04, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, ++ 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, ++ 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0xff, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, ++ 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, ++ 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x00, ++ 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, ++ 0x06, 0x0c, 0x18, 0x30, 0x18, 0x6c, 0x36, 0x18, 0x0c, 0x00, 0x00, 0x60, ++ 0x30, 0x18, 0x0c, 0x18, 0x36, 0x6c, 0x18, 0x30, 0x00, 0x00, 0x7f, 0x36, ++ 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x7e, ++ 0x18, 0x7e, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x30, 0x78, ++ 0x30, 0x72, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, ++ 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, ++ 0x00, 0x00, 0x00, 0x66, 0x66, 0x22, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x36, 0x7f, 0x36, 0x36, 0x36, 0x7f, 0x36, 0x00, 0x00, 0x00, ++ 0x00, 0x66, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, ++ 0x72, 0x56, 0x6c, 0x18, 0x36, 0x6a, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x18, ++ 0x24, 0x28, 0x30, 0x4a, 0x44, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, ++ 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x18, ++ 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x18, 0x18, ++ 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0x7e, 0x3c, ++ 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, ++ 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, ++ 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x40, 0x00, 0x00, 0x00, ++ 0x00, 0x3c, 0x46, 0x4e, 0x5a, 0x72, 0x62, 0x3c, 0x00, 0x00, 0x00, 0x00, ++ 0x18, 0x38, 0x58, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x3c, ++ 0x66, 0x06, 0x0c, 0x18, 0x32, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, ++ 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, ++ 0x66, 0x7e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x60, 0x7c, 0x66, ++ 0x06, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x60, 0x7c, 0x66, 0x66, ++ 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x46, 0x06, 0x0c, 0x18, 0x30, ++ 0x30, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, ++ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x3c, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x08, 0x10, 0x00, ++ 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, ++ 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, ++ 0x06, 0x0c, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x5e, ++ 0x56, 0x5e, 0x40, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x66, ++ 0x7e, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x66, ++ 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, ++ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, ++ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x62, 0x60, 0x78, 0x60, 0x62, 0x7e, 0x00, ++ 0x00, 0x00, 0x00, 0x7e, 0x62, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00, 0x00, ++ 0x00, 0x00, 0x3c, 0x66, 0x60, 0x6e, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, ++ 0x00, 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, ++ 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x7e, ++ 0x46, 0x06, 0x06, 0x06, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6c, ++ 0x78, 0x70, 0x78, 0x6c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, ++ 0x60, 0x60, 0x62, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x41, 0x63, 0x77, 0x7f, ++ 0x6b, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x76, 0x7e, 0x6e, ++ 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, ++ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, ++ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x6e, 0x3c, 0x02, ++ 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x00, 0x00, ++ 0x00, 0x00, 0x3c, 0x66, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00, 0x00, 0x00, ++ 0x00, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, ++ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x66, ++ 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, ++ 0x63, 0x6b, 0x6b, 0x7f, 0x36, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x3c, ++ 0x18, 0x3c, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, ++ 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x46, 0x0c, 0x18, 0x30, ++ 0x62, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, ++ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, ++ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, ++ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, ++ 0x00, 0x08, 0x10, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x60, ++ 0x60, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, ++ 0x3e, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, ++ 0x7e, 0x60, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0x78, ++ 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x66, ++ 0x3e, 0x06, 0x3c, 0x00, 0x00, 0x60, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, ++ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x1c, 0x00, ++ 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x6c, 0x38, ++ 0x00, 0x00, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x7c, 0x66, 0x00, 0x00, 0x00, ++ 0x00, 0x60, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x76, 0x7f, 0x6b, 0x6b, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, ++ 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x66, ++ 0x66, 0x66, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x72, 0x60, ++ 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x60, 0x3c, 0x06, ++ 0x3c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x78, 0x30, 0x30, 0x36, 0x1c, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3a, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x63, 0x6b, 0x6b, 0x6b, 0x36, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x3c, 0x00, 0x00, 0x00, ++ 0x00, 0x7e, 0x0c, 0x18, 0x30, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, ++ 0x18, 0x30, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, ++ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x30, 0x18, 0x18, 0x0c, ++ 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x42, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00}; +diff -ruN proll_18.orig/src/le.c proll-patch-15/src/le.c +--- proll_18.orig/src/le.c 2002-07-23 05:52:49.000000000 +0000 ++++ proll-patch-15/src/le.c 2005-04-16 06:16:20.000000000 +0000 +@@ -185,8 +185,6 @@ + unsigned short rap; /* register address port */ + }; + +-int sparc_lance_debug = 2; +- + /* The Lance uses 24 bit addresses */ + /* On the Sun4c the DVMA will provide the remaining bytes for us */ + /* On the Sun4m we have to instruct the ledma to provide them */ +@@ -771,7 +769,7 @@ + /* Clear the slack of the packet, do I need this? */ + /* For a firewall its a good idea - AC */ + if (len != skblen) +- bzero((char *) &ib->tx_buf [entry][skblen], len - skblen); ++ memset((char *) &ib->tx_buf [entry][skblen], 0, len - skblen); + + /* Now, give the packet to the lance */ + ib->btx_ring [entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN); +diff -ruN proll_18.orig/src/net.h proll-patch-15/src/net.h +--- proll_18.orig/src/net.h 1999-12-15 17:20:17.000000000 +0000 ++++ proll-patch-15/src/net.h 2005-08-14 10:17:02.000000000 +0000 +@@ -124,7 +124,7 @@ + extern int udp_open __P((t_ipaddr daddr, int source, int dest)); + + /* Read from a UDP socket */ +-extern int udp_read __P((char *buf, int bufsize, int timeout, char abortch)); ++extern int udp_read(char *buf, unsigned int bufsize, int timeout); + + /* Write to a UDP socket */ + extern int udp_write __P((char *buf, int writelen)); +diff -ruN proll_18.orig/src/netinit.c proll-patch-15/src/netinit.c +--- proll_18.orig/src/netinit.c 2002-09-13 21:53:33.000000000 +0000 ++++ proll-patch-15/src/netinit.c 2004-11-13 15:50:49.000000000 +0000 +@@ -49,13 +49,20 @@ + unsigned char myhwaddr[ETH_ALEN]; /* my own hardware addr */ + t_ipaddr myipaddr; /* my own IP address */ + t_ipaddr mynetmask; /* my own netmask */ +- char *net_module_name; /* name of init module */ + t_ipaddr servaddr; /* IP of RARP&TFTP server */ + + /* Broadcast hardware address */ +-unsigned char bcasthw[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; ++const unsigned char bcasthw[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + ++unsigned int seed; ++ ++/* This is taken from x86 to be used in network kernel. Returns 15 bits. */ ++short int random() ++{ ++ seed = (seed + 23968)*0x015A4E35 >> 1; ++ return seed & 0x7FFF; ++} + + /* + ************************************************************************** +@@ -104,10 +111,17 @@ + */ + void init_net() + { ++ /* Avoid data segment allocations */ ++ seed = 151; ++ + /* Initialize the different network layer modules */ + init_packet(); +- if (!init_arp() || !init_udp()) { +- printf("\nERROR: init_%s\n", net_module_name); ++ if (!init_arp()) { ++ printf("\nERROR: init_arp\n"); ++ fatal(); ++ } ++ if (!init_udp()) { ++ printf("\nERROR: init_udp\n"); + fatal(); + } + } +diff -ruN proll_18.orig/src/netpriv.h proll-patch-15/src/netpriv.h +--- proll_18.orig/src/netpriv.h 1999-04-27 05:39:37.000000000 +0000 ++++ proll-patch-15/src/netpriv.h 2005-08-14 10:12:20.000000000 +0000 +@@ -83,7 +83,7 @@ + */ + struct device *dev; + char *data; +- int len; ++ unsigned int len; + int protocol; + unsigned char ip_summed; + }; +@@ -130,10 +130,9 @@ + * + */ + extern unsigned char myhwaddr[ETH_ALEN]; /* my own hardware address */ +-extern unsigned char bcasthw[ETH_ALEN]; /* broadcast hardware addr */ ++extern const unsigned char bcasthw[ETH_ALEN]; /* broadcast hardware addr */ + extern t_ipaddr myipaddr; /* my own IP address */ + extern t_ipaddr mynetmask; /* netmask for my network */ +-extern char *net_module_name; /* initialized module's name */ + extern t_ipaddr servaddr; /* server IP address */ + + +@@ -150,7 +149,7 @@ + extern unsigned char *reg_type __P((int typeval, int (* receive)())); + + /* Write a packet to the network */ +-extern int write_packet __P((int bufsize, int typeval, unsigned char *addr)); ++extern int write_packet __P((int bufsize, int typeval, const unsigned char *addr)); + + /* Empty read buffer */ + extern void empty_buf __P((void)); +diff -ruN proll_18.orig/src/openprom.h proll-patch-15/src/openprom.h +--- proll_18.orig/src/openprom.h 2002-07-14 02:26:30.000000000 +0000 ++++ proll-patch-15/src/openprom.h 2005-05-13 16:23:14.000000000 +0000 +@@ -54,29 +54,29 @@ + }; + + struct linux_mem_v0 { +- struct linux_mlist_v0 **v0_totphys; +- struct linux_mlist_v0 **v0_prommap; +- struct linux_mlist_v0 **v0_available; /* What we can use */ ++ struct linux_mlist_v0 * const *v0_totphys; ++ struct linux_mlist_v0 * const *v0_prommap; ++ struct linux_mlist_v0 * const *v0_available; /* What we can use */ + }; + + /* Arguments sent to the kernel from the boot prompt. */ + struct linux_arguments_v0 { +- char *argv[8]; ++ const char *argv[8]; + char args[100]; + char boot_dev[2]; + int boot_dev_ctrl; + int boot_dev_unit; + int dev_partition; +- char *kernel_file_name; ++ const char *kernel_file_name; + void *aieee1; /* XXX */ + }; + + /* V2 and up boot things. */ + struct linux_bootargs_v2 { +- char **bootpath; +- char **bootargs; +- int *fd_stdin; +- int *fd_stdout; ++ const char **bootpath; ++ const char **bootargs; ++ const int *fd_stdin; ++ const int *fd_stdout; + }; + + /* The top level PROM vector. */ +@@ -91,13 +91,13 @@ + struct linux_mem_v0 pv_v0mem; + + /* Node operations. */ +- struct linux_nodeops *pv_nodeops; ++ const struct linux_nodeops *pv_nodeops; + + char **pv_bootstr; + struct linux_dev_v0_funcs pv_v0devops; + +- char *pv_stdin; +- char *pv_stdout; ++ const char *pv_stdin; ++ const char *pv_stdout; + #define PROMDEV_KBD 0 /* input from keyboard */ + #define PROMDEV_SCREEN 0 /* output to screen */ + #define PROMDEV_TTYA 1 /* in/out to ttya */ +@@ -127,7 +127,7 @@ + void (*v2_eval)(char *str); + } pv_fortheval; + +- struct linux_arguments_v0 **pv_v0bootargs; ++ const struct linux_arguments_v0 * const *pv_v0bootargs; + + /* Get ether address. */ + unsigned int (*pv_enaddr)(int d, char *enaddr); +@@ -175,7 +175,7 @@ + int (*no_proplen)(int node, char *name); + int (*no_getprop)(int node, char *name, char *val); + int (*no_setprop)(int node, char *name, char *val, int len); +- char * (*no_nextprop)(int node, char *name); ++ const char * (*no_nextprop)(int node, char *name); + }; + + /* More fun PROM structures for device probing. */ +diff -ruN proll_18.orig/src/packet.c proll-patch-15/src/packet.c +--- proll_18.orig/src/packet.c 2000-02-11 04:56:45.000000000 +0000 ++++ proll-patch-15/src/packet.c 2005-08-14 10:12:49.000000000 +0000 +@@ -41,7 +41,7 @@ + int aligner; + } wbuf; + static struct sk_buff *rskb; +-static int nqskb = 0; ++static int nqskb; + + + void init_packet() +@@ -62,6 +62,8 @@ + for (i = 0; i < MAXSKBS; i++) { + skev[i].skb.allocn = i; + } ++ ++ nqskb = 0; + } + + unsigned char *reg_type(int ptype, int (*func)()) +@@ -81,7 +83,7 @@ + return wbuf.s; + } + +-int write_packet(int leng, int type, unsigned char *dst) ++int write_packet(int leng, int type, const unsigned char *dst) + { + struct sk_buff *skb; + unsigned char *s; +@@ -209,12 +211,12 @@ + /* + */ + void +-eth_copy_and_sum(struct sk_buff *dest, unsigned char *src, int len, int base) ++eth_copy_and_sum(struct sk_buff *dest, unsigned char *src, int len, __attribute__((unused)) int base) + { + bcopy(src, dest->data, len); + } + +-unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev) ++unsigned short eth_type_trans(struct sk_buff *skb, __attribute__((unused)) struct device *dev) + { + unsigned char *s = skb->data + 12; + return s[0] << 8 | s[1]; /* Network order word */ +diff -ruN proll_18.orig/src/printf.c proll-patch-15/src/printf.c +--- proll_18.orig/src/printf.c 1999-03-19 07:03:59.000000000 +0000 ++++ proll-patch-15/src/printf.c 2005-08-14 10:07:26.000000000 +0000 +@@ -19,7 +19,7 @@ + static void printn(struct prf_fp *, unsigned long, unsigned int); + static void putchar(char, struct prf_fp *); + +-static char hextab[] = "0123456789ABCDEF"; ++static const char hextab[] = "0123456789ABCDEF"; + + /* + * Scaled down version of C Library printf. +@@ -41,7 +41,7 @@ + void + prf(struct prf_fp *filog, char *fmt, va_list adx) + { +- register c; ++ register int c; + char *s; + + for(;;) { +@@ -60,7 +60,7 @@ + putchar(va_arg(adx,unsigned), filog); + } else if(c == 's') { + s = va_arg(adx,char*); +- while(c = *s++) ++ while((c = *s++)) + putchar(c,filog); + } else if (c == 'l' || c == 'O') { + printn(filog, (long)va_arg(adx,long), c=='l'?10:8); +@@ -77,10 +77,6 @@ + char prbuf[24]; + register char *cp; + +- if (b == 10 && n < 0) { +- putchar('-',filog); +- n = (~n) + 1; /* n = -n */ +- } + cp = prbuf; + do + *cp++ = hextab[(unsigned int)(n%b)]; +diff -ruN proll_18.orig/src/rconsole.c proll-patch-15/src/rconsole.c +--- proll_18.orig/src/rconsole.c 1999-01-16 07:16:55.000000000 +0000 ++++ proll-patch-15/src/rconsole.c 2005-08-14 10:25:53.000000000 +0000 +@@ -28,12 +28,18 @@ + * move to California. Only plain lat7 survived. + * I recreated lat7-1 changes in lat7-2. --zaitcev + */ ++#ifdef ORIG + #include "lat7_2.bm" /* lat7_1.bm */ ++#else ++#include "lat7_2_swapped.bm" /* lat7_1.bm */ ++#endif + #define LAT7_NCHARS 128 + #define LAT7_HEIGHT 11 + #define LAT7_WIDTH 8 + ++#ifdef ORIG + static Rf_scan lat7_body[ LAT7_NCHARS*LAT7_HEIGHT ]; ++#endif + + #if 1 + /* +@@ -46,18 +52,18 @@ + #endif + + static __inline__ void stfb_w(void *ptr, unsigned int data) { +- __asm__ __volatile__ ("sta %0, [%1] %2" : : ++ __asm__ __volatile__ ("sta %0, [%1] %2\n\t" : : + "r" (data), "r" (ptr), "i" (ASI_M_BYPASS)); + } + + static __inline__ void stfb_b(void *ptr, unsigned int data) { +- __asm__ __volatile__ ("stba %0, [%1] %2" : : ++ __asm__ __volatile__ ("stba %0, [%1] %2\n\t" : : + "r" (data), "r" (ptr), "i" (ASI_M_BYPASS)); + } + + static __inline__ unsigned int ldfb_w(void *ptr) { + unsigned int data; +- __asm__ __volatile__ ("lda [%1] %2, %0" : ++ __asm__ __volatile__ ("lda [%1] %2, %0\n\t" : + "=r" (data) : + "r" (ptr), "i" (ASI_M_BYPASS)); + return data; +@@ -65,7 +71,7 @@ + + static __inline__ unsigned int ldfb_b(void *ptr) { + unsigned int data; +- __asm__ __volatile__ ("lduba [%1] %2, %0" : ++ __asm__ __volatile__ ("lduba [%1] %2, %0\n\t" : + "=r" (data) : + "r" (ptr), "i" (ASI_M_BYPASS)); + return data; +@@ -94,6 +100,7 @@ + + #endif + ++#ifdef ORIG + static inline int swapbits(int w0) + { + int w1 = 0; +@@ -105,13 +112,16 @@ + } + return w1; + } ++#endif + + void font_cons_7(struct rfont *p) + { ++#ifdef ORIG + int x; + int col = 0; + int row = 0; + int erow = 0; ++ + for (x = 0; x < LAT7_NCHARS*LAT7_HEIGHT; x++ ) { + lat7_body[ (erow * lat7_2_width/8 + col) * LAT7_HEIGHT + row ] = + swapbits(lat7_2_bits[x]) & 0xFF; +@@ -124,6 +134,9 @@ + } + } + p->body_ = lat7_body; ++#else ++ p->body_ = lat7_2_bits; ++#endif + p->nchars_ = LAT7_NCHARS; + p->width_ = LAT7_WIDTH; + p->height_ = LAT7_HEIGHT; +@@ -175,7 +188,7 @@ + r->render_ = p->render_; + } + +-void raster_dest(struct raster *r) ++void raster_dest(__attribute((unused)) struct raster *r) + { + } + +diff -ruN proll_18.orig/src/rconsole.h proll-patch-15/src/rconsole.h +--- proll_18.orig/src/rconsole.h 1999-01-16 05:00:59.000000000 +0000 ++++ proll-patch-15/src/rconsole.h 2004-11-13 15:50:49.000000000 +0000 +@@ -13,10 +13,10 @@ + */ + + #define RF_MAXWIDTH 16 +-typedef unsigned short Rf_scan; /* __w16 to be used */ ++typedef unsigned char Rf_scan; /* __w16 to be used */ + + struct rfont { +- Rf_scan *body_; ++ const Rf_scan *body_; + int nchars_; /* 128 for ASCII ... 65536 for Unicode */ + int width_; /* [Pixels]. Maximum size is 16. */ + int height_; /* [Pixels == scan lines]. */ +diff -ruN proll_18.orig/src/romlib.h proll-patch-15/src/romlib.h +--- proll_18.orig/src/romlib.h 1999-04-20 04:26:45.000000000 +0000 ++++ proll-patch-15/src/romlib.h 2005-04-16 20:32:49.000000000 +0000 +@@ -72,13 +72,13 @@ + */ + #define memcpy(dst, src, len) bcopy(src, dst, len) + #define memcmp(x1, x2, len) bcmp(x1, x2, len) +-#define memset(p, len, zero) bzero(p, len) +-extern void bcopy(void *b1, void *b2, int length); +-extern int bcmp(void *b1, void *b2, int length); +-extern void bzero(void *b, int c); ++extern void bcopy(const void *b1, void *b2, int length); ++extern int bcmp(const void *b1, const void *b2, int length); ++typedef unsigned int size_t; ++extern void *memset(void *p, int c, size_t len); + /* gcc complains about "conflicting types for builtin function strlen". */ + #define strlen(s) ssize(s) +-extern int ssize(char *s); ++extern int ssize(const char *s); + + + /* +diff -ruN proll_18.orig/src/sched_4m.c proll-patch-15/src/sched_4m.c +--- proll_18.orig/src/sched_4m.c 1999-04-27 05:48:51.000000000 +0000 ++++ proll-patch-15/src/sched_4m.c 2005-08-14 10:18:14.000000000 +0000 +@@ -108,7 +108,7 @@ + static int set_bolt; /* Tick counter limit */ + static struct handsc hndv[16]; + +-static unsigned int intr_to_mask[16] = { ++static unsigned const int intr_to_mask[16] = { + 0, 0, 0, 0, 0, 0, SUN4M_INT_ETHERNET, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; +@@ -130,7 +130,7 @@ + int /* 0 - not expired yet; <>0 - timer expired */ + chk_timeout() + { +- int lim = (((1000000/HZ) + 1) << 10); ++ unsigned int lim = (((1000000/HZ) + 1) << 10); + unsigned int clear; + unsigned int intc; + int n; +@@ -182,7 +182,7 @@ + struct handsc *hndp; + unsigned int mask; + +- if (irq < 0 || irq >= 16) { ++ if (irq == 0 || irq >= 16) { + printk("request_irq: bad irq %d\n", irq); + return -1; + } +@@ -207,7 +207,7 @@ + { + struct handsc *hndp; + +- if (irq < 0 || irq >= 16) { ++ if (irq == 0 || irq >= 16) { + printk("free_irq: bad irq %d\n", irq); + return; + } +diff -ruN proll_18.orig/src/swap.c proll-patch-15/src/swap.c +--- proll_18.orig/src/swap.c 1970-01-01 00:00:00.000000000 +0000 ++++ proll-patch-15/src/swap.c 2004-11-13 15:50:49.000000000 +0000 +@@ -0,0 +1,21 @@ ++// Convert the lat7 font so that no conversion is needed at runtime. ++#define ORIG ++#include "rconsole.c" ++ ++#include ++ ++int main() ++{ ++ struct rfont p; ++ int i; ++ ++ font_cons_7(&p); ++ ++ printf(" "); ++ for (i = 0; i < LAT7_NCHARS*LAT7_HEIGHT; i++) { ++ printf("0x%02x, ", p.body_[i]); ++ if ((i % 12) == 11) ++ printf("\n "); ++ } ++ printf("\n"); ++} +diff -ruN proll_18.orig/src/system.c proll-patch-15/src/system.c +--- proll_18.orig/src/system.c 2002-07-23 05:52:49.000000000 +0000 ++++ proll-patch-15/src/system.c 2005-04-16 06:16:20.000000000 +0000 +@@ -298,8 +298,8 @@ + } + + /* We need to start from LOADBASE, but kernel wants PAGE_SIZE. */ +- pa = PAGE_SIZE; +- for (va = PAGE_SIZE; va < LOWMEMSZ; va += PAGE_SIZE) { ++ pa = 0; ++ for (va = 0; va < LOWMEMSZ; va += PAGE_SIZE) { + map_page(l1, va, pa, 0, highbase); + pa += PAGE_SIZE; + } +@@ -507,30 +507,44 @@ + { + char *p; + +- if ((p = mem_alloc(t, size, align)) != 0) bzero(p, size); ++ if ((p = mem_alloc(t, size, align)) != 0) memset(p, 0, size); + return p; + } + + /* + * Library functions + */ +-void bzero(void *s, int len) { +- while (len--) *((char *)s)++ = 0; ++void *memset(void *s, int c, size_t len) ++{ ++ void *p = s; ++ ++ while (len--) { ++ *(char *)s = c; ++ s++; ++ } ++ return p; + } + +-void bcopy(void *f, void *t, int len) { +- while (len--) *((char *)t)++ = *((char *)f)++; ++void bcopy(const void *f, void *t, int len) { ++ while (len--) { ++ *(char *)t = *(char *)f; ++ f++; ++ t++; ++ } + } + + /* Comparison is 7-bit */ +-int bcmp(void *s1, void *s2, int len) ++int bcmp(const void *s1, const void *s2, int len) + { + int i; + char ch; + + while (len--) { +- ch = *((char *)s1)++; +- if ((i = ch - *((char *)s2)++) != 0) ++ ch = *(char *)s1; ++ i = ch - *(char *)s2; ++ s1++; ++ s2++; ++ if (i != 0) + return i; + if (ch == 0) + return 0; +@@ -538,8 +552,8 @@ + return 0; + } + +-int strlen(char *s) { +- char *p; ++int strlen(const char *s) { ++ const char *p; + for (p = s; *p != 0; p++) { } + return p - s; + } +@@ -560,14 +574,6 @@ + va_end(x1); + } + +-/* This is taken from x86 to be used in network kernel. Returns 15 bits. */ +-short int random() +-{ +- static unsigned int seed = 151; +- seed = (seed + 23968)*0x015A4E35 >> 1; +- return seed & 0x7FFF; +-} +- + void fatal() + { + printk("fatal."); +diff -ruN proll_18.orig/src/system.h proll-patch-15/src/system.h +--- proll_18.orig/src/system.h 2002-09-13 21:53:32.000000000 +0000 ++++ proll-patch-15/src/system.h 2005-04-16 06:16:20.000000000 +0000 +@@ -16,7 +16,7 @@ + #define IOMAPSIZE (1*1024*1024) /* 1 Meg maximum: we do not map framebuffer. */ + #define NCTX_SWIFT 0x100 + +-#define MAX_BANKS 3 /* Allocation for all machines */ ++#define MAX_BANKS 8 /* Allocation for all machines */ + + #ifndef __ASSEMBLY__ + struct bank { +@@ -164,10 +164,10 @@ + + extern __inline__ void setipl(unsigned long __orig_psr) + { +- __asm__ __volatile__(" +- wr %0, 0x0, %%psr +- nop; nop; nop +-" : /* no outputs */ ++ __asm__ __volatile__( ++ "wr %0, 0x0, %%psr\n\t" ++ "nop; nop; nop\n\t" ++ : /* no outputs */ + : "r" (__orig_psr) + : "memory", "cc"); + } +@@ -176,13 +176,13 @@ + { + unsigned long tmp; + +- __asm__ __volatile__(" +- rd %%psr, %0 +- nop; nop; nop; /* Sun4m + Cypress + SMP bug */ +- or %0, %1, %0 +- wr %0, 0x0, %%psr +- nop; nop; nop +-" : "=r" (tmp) ++ __asm__ __volatile__( ++ "rd %%psr, %0\n\t" ++ "nop; nop; nop;\n\t" /* Sun4m + Cypress + SMP bug */ ++ "or %0, %1, %0\n\t" ++ "wr %0, 0x0, %%psr\n\t" ++ "nop; nop; nop\n\t" ++ : "=r" (tmp) + : "i" (PSR_PIL) + : "memory"); + } +@@ -191,13 +191,13 @@ + { + unsigned long tmp; + +- __asm__ __volatile__(" +- rd %%psr, %0 +- nop; nop; nop; /* Sun4m + Cypress + SMP bug */ +- andn %0, %1, %0 +- wr %0, 0x0, %%psr +- nop; nop; nop +-" : "=r" (tmp) ++ __asm__ __volatile__( ++ "rd %%psr, %0\n\t" ++ "nop; nop; nop;\n\t" /* Sun4m + Cypress + SMP bug */ ++ "andn %0, %1, %0\n\t" ++ "wr %0, 0x0, %%psr\n\t" ++ "nop; nop; nop\n\t" ++ : "=r" (tmp) + : "i" (PSR_PIL) + : "memory"); + } +@@ -214,18 +214,18 @@ + { + unsigned long retval; + +- __asm__ __volatile__(" +- rd %%psr, %0 +- nop; nop; nop; /* Sun4m + Cypress + SMP bug */ +- and %0, %2, %%g1 +- and %1, %2, %%g2 +- xorcc %%g1, %%g2, %%g0 +- be 1f +- nop +- wr %0, %2, %%psr +- nop; nop; nop; +-1: +-" : "=r" (retval) ++ __asm__ __volatile__( ++ "rd %%psr, %0\n\t" ++ "nop; nop; nop;\n\t" /* Sun4m + Cypress + SMP bug */ ++ "and %0, %2, %%g1\n\t" ++ "and %1, %2, %%g2\n\t" ++ "xorcc %%g1, %%g2, %%g0\n\t" ++ "be 1f\n\t" ++ "nop\n\t" ++ "wr %0, %2, %%psr\n\t" ++ "nop; nop; nop;\n\t" ++ "1:\n\t" ++ : "=r" (retval) + : "r" (__new_psr), "i" (PSR_PIL) + : "g1", "g2", "memory", "cc"); + +@@ -236,13 +236,13 @@ + { + unsigned long retval; + +- __asm__ __volatile__(" +- rd %%psr, %0 +- nop; nop; nop; /* Sun4m + Cypress + SMP bug */ +- or %0, %1, %%g1 +- wr %%g1, 0x0, %%psr +- nop; nop; nop +-" : "=r" (retval) ++ __asm__ __volatile__( ++ "rd %%psr, %0\n\t" ++ "nop; nop; nop;\n\t" /* Sun4m + Cypress + SMP bug */ ++ "or %0, %1, %%g1\n\t" ++ "wr %%g1, 0x0, %%psr\n\t" ++ "nop; nop; nop\n\t" ++ : "=r" (retval) + : "i" (PSR_PIL) + : "g1", "memory"); + +diff -ruN proll_18.orig/src/tftp.c proll-patch-15/src/tftp.c +--- proll_18.orig/src/tftp.c 2002-09-13 21:53:34.000000000 +0000 ++++ proll-patch-15/src/tftp.c 2005-08-14 10:16:15.000000000 +0000 +@@ -127,7 +127,7 @@ + int len; + + /* Read packet with timeout */ +- len = udp_read((char *)(&inpbuf), sizeof(inpbuf), TFTP_TIMEOUT, CHR_ESC); ++ len = udp_read((char *)(&inpbuf), sizeof(inpbuf), TFTP_TIMEOUT); + if (len == 0) { + printf("TFTP: Timeout\n"); + return(ERR_TIMEOUT); +diff -ruN proll_18.orig/src/udp.c proll-patch-15/src/udp.c +--- proll_18.orig/src/udp.c 2001-12-24 05:12:53.000000000 +0000 ++++ proll-patch-15/src/udp.c 2005-08-14 10:17:19.000000000 +0000 +@@ -76,12 +76,9 @@ + * + * Open a new UDP socket. + */ +-int udp_open(daddr, source, dest) +-t_ipaddr daddr; +-int source; +-int dest; ++int udp_open(t_ipaddr daddr, int source, int dest) + { +- register unsigned char *addr; ++ const unsigned char *addr; + + /* Set global variables */ + usource = source; +@@ -101,16 +98,13 @@ + * + * IP receiver routine + */ +-static int ip_recv(buf, bufsize, addr) +-unsigned char *buf; +-int bufsize; +-unsigned char *addr; ++static int ip_recv(unsigned char *buf, unsigned int bufsize, unsigned char *addr) + { + struct iphdr *ipp = ((struct iphdr *)buf); + struct udphdr *udpp = ((struct udphdr *)(buf + IP_MIN_HSIZE)); + struct udp_pseudo psehdr; + +- int size; ++ unsigned int size; + t_ipaddr dadr; + + #ifdef DEBUG +@@ -194,13 +188,9 @@ + * + * Read one packet from a UDP socket + */ +-int udp_read(buf, bufsize, timeout, abortch) +-char *buf; +-int bufsize; +-int timeout; +-char abortch; ++int udp_read(char *buf, unsigned int bufsize, int timeout) + { +- int len; ++ unsigned int len; + + /* Wait until we get something */ + set_timeout(timeout); +@@ -299,9 +289,6 @@ + */ + int init_udp() + { +- /* Set module name for error handling */ +- net_module_name = "udp"; +- + /* Register IP packet type and set write buffer pointer */ + if ((writebuf = reg_type(htons(ETH_P_IP), ip_recv)) == NULL) + return(FALSE); +diff -ruN proll_18.orig/src/udp.h proll-patch-15/src/udp.h +--- proll_18.orig/src/udp.h 2001-12-24 05:12:34.000000000 +0000 ++++ proll-patch-15/src/udp.h 2005-08-14 10:16:40.000000000 +0000 +@@ -53,7 +53,7 @@ + extern int udp_open __P((t_ipaddr daddr, int source, int dest)); + + /* Read from a UDP socket */ +-extern int udp_read __P((char *buf, int bufsize, int timeout, char abortch)); ++extern int udp_read(char *buf, unsigned int bufsize, int timeout); + + /* Write to a UDP socket */ + extern int udp_write __P((char *buf, int writelen)); +diff -ruN proll_18.orig/src/vcons_zs.c proll-patch-15/src/vcons_zs.c +--- proll_18.orig/src/vcons_zs.c 1970-01-01 00:00:00.000000000 +0000 ++++ proll-patch-15/src/vcons_zs.c 2005-08-14 10:25:51.000000000 +0000 +@@ -0,0 +1,68 @@ ++/** ++ ** Console over 'zs' (Zilog serial port) ++ ** Copyright 1999 Pete Zaitcev ++ ** This code is licensed under GNU General Public License. ++ **/ ++ ++#include "vconsole.h" ++#include ++ ++#define ZS_DATA 0x02 ++ ++int vcon_zs_init(struct vconterm *t, unsigned int a0) ++{ ++ ++ t->impl = (void *) a0; ++ ++ t->vc_x = 0; t->vc_y = 0; ++ t->backp = 0; t->backc = 0; ++ ++ stb_bypass(a0, 3); // reg 3 ++ stb_bypass(a0, 1); // enable rx ++ ++ stb_bypass(a0, 5); // reg 5 ++ stb_bypass(a0, 8); // enable tx ++ ++ return 0; ++} ++ ++int vcon_zs_putch(struct vconterm *t, char c) ++{ ++ unsigned zs_ptr = (unsigned) t->impl; ++ ++ //while ((ldb_bypass(zs_ptr + ZS_LSR) & 0x60) != 0x60) { } ++ stb_bypass(zs_ptr + ZS_DATA, c); ++ return 0; ++} ++ ++int vcon_zs_write(struct vconterm *t, char *data, int leng) ++{ ++ while (leng != 0) { ++ leng--; ++ vcon_zs_putch(t, *data++); ++ } ++ return leng; ++} ++ ++int vcon_zs_read(struct vconterm *t, char *data, __attribute((unused)) int leng) ++{ ++ unsigned zs_ptr = (unsigned) t->impl; ++ ++ while ((ldb_bypass(zs_ptr) & 1) != 1) { } ++ *data = ldb_bypass(zs_ptr + ZS_DATA); ++ return 0; ++} ++ ++int vcon_zs_getch(struct vconterm *t) ++{ ++ unsigned zs_ptr = (unsigned) t->impl; ++ ++ while ((ldb_bypass(zs_ptr) & 1) != 1) { } ++ return ldb_bypass(zs_ptr + ZS_DATA) & 0xff; ++} ++ ++void vcon_zs_fini(__attribute((unused)) struct vconterm *t) ++{ ++ /* violent crash in the end */ ++ ; ++} +diff -ruN proll_18.orig/src/vconsole.c proll-patch-15/src/vconsole.c +--- proll_18.orig/src/vconsole.c 1999-11-08 03:10:28.000000000 +0000 ++++ proll-patch-15/src/vconsole.c 2005-08-14 10:24:49.000000000 +0000 +@@ -7,12 +7,17 @@ + #include "vconsole.h" + + #include "hconsole.h" ++#include + + static void vcon_i_cursfeed(struct vconterm *t); + static void vcon_i_backflush(struct vconterm *t); + + struct hconsole hcons0; + ++enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, ++ EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd, ++ ESpalette }; ++ + int vcon_init(struct vconterm *t, unsigned int a0) + { + struct hconsole *hconp; +@@ -25,11 +30,49 @@ + + t->vc_x = 0; t->vc_y = 0; + t->backp = 0; t->backc = 0; ++ t->vc_state = ESnormal; + + hcon_clear(hconp, 0, 0, hconp->ydim_, hconp->xdim_); + return 0; + } + ++/* ++ * gotoxy() must verify all boundaries, because the arguments ++ * might also be negative. If the given position is out of ++ * bounds, the cursor is placed at the nearest margin. ++ */ ++static void gotoxy(struct vconterm *vc, int new_x, int new_y) ++{ ++ int max_x, max_y; ++ struct hconsole *hconp = vc->impl; ++ ++ max_x = hcon_qxdim(hconp); ++ max_y = hcon_qydim(hconp); ++ ++ if (new_x < 0) ++ vc->vc_x = 0; ++ else { ++ if (new_x >= max_x) ++ vc->vc_x = max_x - 1; ++ else ++ vc->vc_x = new_x; ++ } ++ ++ if (new_y < 0) ++ vc->vc_y = 0; ++ else if (new_y >= max_y) ++ vc->vc_y = max_y - 1; ++ else ++ vc->vc_y = new_y; ++ ++} ++ ++/* for absolute user moves, when decom is set */ ++static void gotoxay(struct vconterm *t, int new_x, int new_y) ++{ ++ gotoxy(t, new_x, new_y); ++} ++ + int vcon_write(struct vconterm *t, char *data, int leng) + { + int l = leng; +@@ -40,29 +83,101 @@ + if (l <= 0) break; + c = *data++; --l; + +- switch (c) { +- case 0x07: /* Bell */ +- vcon_i_backflush(t); +- break; +- case 0x0A: /* Linefeed */ +- vcon_i_backflush(t); +- vcon_i_cursfeed(t); ++ switch(t->vc_state) { ++ case ESesc: ++ t->vc_state = ESnormal; ++ switch (c) { ++ case '[': ++ t->vc_state = ESsquare; ++ break; ++ case 'M': ++ hcon_scroll(hconp, 0, hcon_qydim(hconp), SM_UP, 1); ++ break; ++ default: ++ //printk("Unhandled escape code '%c'\n", c); ++ break; ++ } + break; +- case 0x0D: /* Return */ +- vcon_i_backflush(t); +- t->vc_x = 0; ++ case ESsquare: ++ for(t->vc_npar = 0 ; t->vc_npar < NPAR ; t->vc_npar++) ++ t->vc_par[t->vc_npar] = 0; ++ t->vc_npar = 0; ++ t->vc_state = ESgetpars; ++ case ESgetpars: ++ if (c==';' && t->vc_nparvc_npar++; ++ break; ++ } else if (c>='0' && c<='9') { ++ t->vc_par[t->vc_npar] *= 10; ++ t->vc_par[t->vc_npar] += c-'0'; ++ break; ++ } else t->vc_state=ESgotpars; ++ case ESgotpars: ++ t->vc_state = ESnormal; ++ switch(c) { ++ case 'H': case 'f': ++ if (t->vc_par[0]) t->vc_par[0]--; ++ if (t->vc_par[1]) t->vc_par[1]--; ++ gotoxay(t, t->vc_par[1], t->vc_par[0]); ++ break; ++ case 'J': ++ if (t->vc_par[0] == 0) { ++ //erase from cursor to end of display ++ hcon_clear(hconp, t->vc_y, t->vc_x, hconp->ydim_, hconp->xdim_); ++ } ++ break; ++ case 'M': ++ hcon_scroll(hconp, 0, hcon_qydim(hconp), SM_UP, 1); ++ break; ++ case 'm': ++ break; ++ default: ++#if 0 ++ printk("Unhandled escape code '%c', par[%d, %d, %d, %d, %d]\n", ++ c, t->vc_par[0], t->vc_par[1], t->vc_par[2], t->vc_par[3], t->vc_par[4]); ++#endif ++ break; ++ } + break; + default: +- if (t->backp == 0) { +- t->backc = 1; +- t->backp = data-1; +- } else { +- t->backc++; +- } +- if (t->vc_x + t->backc >= hcon_qxdim(hconp)) { ++ t->vc_state = ESnormal; ++ switch (c) { ++ case 0x07: /* Bell */ ++ vcon_i_backflush(t); ++ break; ++ case 0x08: /* BS */ ++ vcon_i_backflush(t); ++ if (t->vc_x > 0) ++ t->vc_x--; ++ break; ++ case 0x0A: /* Linefeed */ + vcon_i_backflush(t); +- t->vc_x = 0; + vcon_i_cursfeed(t); ++ break; ++ case 0x0D: /* Return */ ++ vcon_i_backflush(t); ++ t->vc_x = 0; ++ break; ++ case 24: case 26: ++ vcon_i_backflush(t); ++ t->vc_state = ESnormal; ++ break; ++ case 27: ++ vcon_i_backflush(t); ++ t->vc_state = ESesc; ++ break; ++ default: ++ if (t->backp == 0) { ++ t->backc = 1; ++ t->backp = data-1; ++ } else { ++ t->backc++; ++ } ++ if ((unsigned int)t->vc_x + t->backc >= hcon_qxdim(hconp)) { ++ vcon_i_backflush(t); ++ t->vc_x = 0; ++ vcon_i_cursfeed(t); ++ } + } + } + } +@@ -73,7 +188,7 @@ + static void vcon_i_cursfeed(struct vconterm *t) { + struct hconsole *hconp = t->impl; + +- if (++t->vc_y >= hcon_qydim(hconp)) { ++ if ((unsigned int)++t->vc_y >= hcon_qydim(hconp)) { + t->vc_y = hcon_qydim(hconp)-1; + hcon_scroll(hconp, 0, hcon_qydim(hconp), SM_UP, 1); + } +@@ -90,22 +205,75 @@ + t->backp = 0; t->backc = 0; + } + +-int vcon_putch(struct vconterm *t, char c) ++int vcon_putch(__attribute__((unused)) struct vconterm *t, __attribute__((unused)) char c) + { + return -1; + } + +-int vcon_read(struct vconterm *t, char *data, int leng) ++int vcon_read(__attribute__((unused)) struct vconterm *t, __attribute__((unused)) char *data, __attribute__((unused)) int leng) + { + return 0; + } + +-int vcon_getch(struct vconterm *t) ++static const unsigned char sunkbd_keycode[128] = { ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0, 8, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, ++ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '\\', 13, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ ' ', ++}; ++ ++static const unsigned char sunkbd_keycode_shifted[128] = { ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0, 8, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, ++ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '|', 13, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ ' ', ++}; ++ ++static int shiftstate; ++ ++int vcon_getch(__attribute__((unused)) struct vconterm *t) + { +- return -1; ++ int ch; ++ ++ while ((ldb_bypass(0x71000004) & 1) != 1) { } ++ do { ++ ch = ldb_bypass(0x71000006) & 0xff; ++ if (ch == 99) ++ shiftstate |= 1; ++ else if (ch == 110) ++ shiftstate |= 2; ++ else if (ch == 227) ++ shiftstate &= ~1; ++ else if (ch == 238) ++ shiftstate &= ~2; ++ //printk("getch: %d\n", ch); ++ } ++ while ((ch & 0x80) == 0 || ch == 238 || ch == 227); // Wait for key release ++ //printk("getch rel: %d\n", ch); ++ ch &= 0x7f; ++ if (shiftstate) ++ ch = sunkbd_keycode_shifted[ch]; ++ else ++ ch = sunkbd_keycode[ch]; ++ //printk("getch xlate: %d\n", ch); ++ return ch; + } + +-void vcon_fini(struct vconterm *t) ++void vcon_fini(__attribute__((unused)) struct vconterm *t) + { + /* violent crash in the end */ + ; +diff -ruN proll_18.orig/src/vconsole.h proll-patch-15/src/vconsole.h +--- proll_18.orig/src/vconsole.h 1999-11-08 00:58:13.000000000 +0000 ++++ proll-patch-15/src/vconsole.h 2005-03-02 12:40:12.000000000 +0000 +@@ -6,6 +6,8 @@ + #ifndef VCONSOLE_H + #define VCONSOLE_H + ++#define NPAR 16 ++ + struct vconterm { + void *impl; + +@@ -13,6 +15,8 @@ + int backc; /* Same, count */ + + int vc_x, vc_y; /* XXX Make vcon_xxx() to use cellmap->xpos_ */ ++ int vc_state; ++ unsigned int vc_npar,vc_par[NPAR]; /* Parameters of current escape sequence */ + }; + + int vcon_init(struct vconterm *t, unsigned int a0); diff --git a/tools/ioemu/pc-bios/vgabios.diff b/tools/ioemu/pc-bios/vgabios.diff new file mode 100644 index 0000000000..73159a0fd0 --- /dev/null +++ b/tools/ioemu/pc-bios/vgabios.diff @@ -0,0 +1,811 @@ +Index: Makefile +=================================================================== +RCS file: /sources/vgabios/vgabios/Makefile,v +retrieving revision 1.17 +diff -u -w -r1.17 Makefile +--- Makefile 6 Mar 2005 13:06:47 -0000 1.17 ++++ Makefile 25 Mar 2006 01:19:02 -0000 +@@ -17,9 +17,9 @@ + all: bios cirrus-bios + + +-bios: biossums vgabios.bin vgabios.debug.bin ++bios: biossums vgabios.bin #vgabios.debug.bin + +-cirrus-bios: vgabios-cirrus.bin vgabios-cirrus.debug.bin ++cirrus-bios: vgabios-cirrus.bin #vgabios-cirrus.debug.bin + + clean: + /bin/rm -f biossums *.o *.s *.ld86 \ +Index: clext.c +=================================================================== +RCS file: /sources/vgabios/vgabios/clext.c,v +retrieving revision 1.9 +diff -u -w -r1.9 clext.c +--- clext.c 4 Dec 2004 15:26:17 -0000 1.9 ++++ clext.c 25 Mar 2006 01:19:03 -0000 +@@ -238,6 +238,21 @@ + 0xffff + }; + ++/* 1600x1200x8 */ ++unsigned short cseq_1600x1200x8[] = { ++0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, ++0x760b,0x760c,0x760d,0x760e, ++0x0412,0x0013,0x2017, ++0x341b,0x341c,0x341d,0x341e, ++0xffff ++}; ++unsigned short ccrtc_1600x1200x8[] = { ++0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707, ++0x6009,0x000c,0x000d, ++0x0310,0xff12,0xa013,0x4014,0xff15,0x2416,0xc317,0xff18, ++0x001a,0x221b,0x001d, ++0xffff ++}; + + cirrus_mode_t cirrus_modes[] = + { +@@ -291,6 +306,10 @@ + cseq_1280x1024x16,cgraph_svgacolor,ccrtc_1280x1024x16,16, + 6,5,11,6,5,5,0,0,0}, + ++ {0x7b,1600,1200,8,0x00, ++ cseq_1600x1200x8,cgraph_svgacolor,ccrtc_1600x1200x8,8, ++ 4,0,0,0,0,0,0,0,0}, ++ + {0xfe,0,0,0,0,cseq_vga,cgraph_vga,ccrtc_vga,0, + 0xff,0,0,0,0,0,0,0,0}, + {0xff,0,0,0,0,0,0,0,0, +Index: vgabios.c +=================================================================== +RCS file: /sources/vgabios/vgabios/vgabios.c,v +retrieving revision 1.63 +diff -u -w -r1.63 vgabios.c +--- vgabios.c 26 Dec 2005 19:50:26 -0000 1.63 ++++ vgabios.c 25 Mar 2006 01:19:03 -0000 +@@ -111,6 +111,7 @@ + static void biosfn_read_video_state_size(); + static void biosfn_save_video_state(); + static void biosfn_restore_video_state(); ++extern Bit8u video_save_pointer_table[]; + + // This is for compiling with gcc2 and gcc3 + #define ASM_START #asm +@@ -459,6 +460,29 @@ + + pop ds + ret ++ ++_video_save_pointer_table: ++ .word _video_param_table ++ .word 0xc000 ++ ++ .word 0 /* XXX: fill it */ ++ .word 0 ++ ++ .word 0 /* XXX: fill it */ ++ .word 0 ++ ++ .word 0 /* XXX: fill it */ ++ .word 0 ++ ++ .word 0 /* XXX: fill it */ ++ .word 0 ++ ++ .word 0 /* XXX: fill it */ ++ .word 0 ++ ++ .word 0 /* XXX: fill it */ ++ .word 0 ++ + ASM_END + + // -------------------------------------------------------------------------------------------- +@@ -780,8 +804,8 @@ + + // Should we clear the screen ? + Bit8u noclearmem=mode&0x80; +- Bit8u line,mmask,*palette; +- Bit16u i,twidth,theight,cheight; ++ Bit8u line,mmask,*palette,vpti; ++ Bit16u i,twidth,theightm1,cheight; + Bit8u modeset_ctl,video_ctl,vga_switches; + Bit16u crtc_addr; + +@@ -804,9 +828,10 @@ + if(line==0xFF) + return; + +- twidth=vga_modes[line].twidth; +- theight=vga_modes[line].theight; +- cheight=vga_modes[line].cheight; ++ vpti=line_to_vpti[line]; ++ twidth=video_param_table[vpti].twidth; ++ theightm1=video_param_table[vpti].theightm1; ++ cheight=video_param_table[vpti].cheight; + + // Read the bios vga control + video_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL); +@@ -866,21 +891,25 @@ + inb(VGAREG_ACTL_RESET); + + // Set Attribute Ctl +- for(i=0;i<=ACTL_MAX_REG;i++) ++ for(i=0;i<=0x13;i++) + {outb(VGAREG_ACTL_ADDRESS,i); +- outb(VGAREG_ACTL_WRITE_DATA,actl_regs[vga_modes[line].actlmodel][i]); ++ outb(VGAREG_ACTL_WRITE_DATA,video_param_table[vpti].actl_regs[i]); + } ++ outb(VGAREG_ACTL_ADDRESS,0x14); ++ outb(VGAREG_ACTL_WRITE_DATA,0x00); + + // Set Sequencer Ctl +- for(i=0;i<=SEQU_MAX_REG;i++) ++ outb(VGAREG_SEQU_ADDRESS,0); ++ outb(VGAREG_SEQU_DATA,0x03); ++ for(i=1;i<=4;i++) + {outb(VGAREG_SEQU_ADDRESS,i); +- outb(VGAREG_SEQU_DATA,sequ_regs[vga_modes[line].sequmodel][i]); ++ outb(VGAREG_SEQU_DATA,video_param_table[vpti].sequ_regs[i - 1]); + } + + // Set Grafx Ctl +- for(i=0;i<=GRDC_MAX_REG;i++) ++ for(i=0;i<=8;i++) + {outb(VGAREG_GRDC_ADDRESS,i); +- outb(VGAREG_GRDC_DATA,grdc_regs[vga_modes[line].grdcmodel][i]); ++ outb(VGAREG_GRDC_DATA,video_param_table[vpti].grdc_regs[i]); + } + + // Set CRTC address VGA or MDA +@@ -889,13 +918,13 @@ + // Disable CRTC write protection + outw(crtc_addr,0x0011); + // Set CRTC regs +- for(i=0;i<=CRTC_MAX_REG;i++) ++ for(i=0;i<=0x18;i++) + {outb(crtc_addr,i); +- outb(crtc_addr+1,crtc_regs[vga_modes[line].crtcmodel][i]); ++ outb(crtc_addr+1,video_param_table[vpti].crtc_regs[i]); + } + + // Set the misc register +- outb(VGAREG_WRITE_MISC_OUTPUT,vga_modes[line].miscreg); ++ outb(VGAREG_WRITE_MISC_OUTPUT,video_param_table[vpti].miscreg); + + // Enable video + outb(VGAREG_ACTL_ADDRESS,0x20); +@@ -927,9 +956,9 @@ + // Set the BIOS mem + write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE,mode); + write_word(BIOSMEM_SEG,BIOSMEM_NB_COLS,twidth); +- write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE,vga_modes[line].slength); ++ write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE,*(Bit16u *)&video_param_table[vpti].slength_l); + write_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS,crtc_addr); +- write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS,theight-1); ++ write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS,theightm1); + write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,cheight); + write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,(0x60|noclearmem)); + write_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES,0xF9); +@@ -937,8 +966,8 @@ + + // FIXME We nearly have the good tables. to be reworked + write_byte(BIOSMEM_SEG,BIOSMEM_DCC_INDEX,0x08); // 8 is VGA should be ok for now +- write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER,0x00); +- write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER+2,0x00); ++ write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER, video_save_pointer_table); ++ write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER+2, 0xc000); + + // FIXME + write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x00); // Unavailable on vanilla vga, but... +@@ -1114,7 +1143,7 @@ + } + else + { +- address = page*vga_modes[line].slength; ++ address = page * (*(Bit16u *)&video_param_table[line_to_vpti[line]].slength_l); + } + + // CRTC regs 0x0c and 0x0d +@@ -1271,7 +1300,7 @@ + else + { + // FIXME gfx mode not complete +- cheight=vga_modes[line].cheight; ++ cheight=video_param_table[line_to_vpti[line]].cheight; + switch(vga_modes[line].memmodel) + { + case PLANAR4: +@@ -1581,7 +1610,7 @@ + else + { + // FIXME gfx mode not complete +- cheight=vga_modes[line].cheight; ++ cheight=video_param_table[line_to_vpti[line]].cheight; + bpp=vga_modes[line].pixbits; + while((count-->0) && (xcurs0) && (xcurs /proc/sys/fs/binfmt_misc/register + echo ':i486:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-i386:' > /proc/sys/fs/binfmt_misc/register +fi +if [ $cpu != "arm" ] ; then + echo ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-arm:' > /proc/sys/fs/binfmt_misc/register + echo ':armeb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-armeb:' > /proc/sys/fs/binfmt_misc/register +fi +if [ $cpu != "sparc" ] ; then + echo ':sparc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-sparc:' > /proc/sys/fs/binfmt_misc/register +fi +if [ $cpu != "ppc" ] ; then + echo ':ppc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-ppc:' > /proc/sys/fs/binfmt_misc/register +fi +if [ $cpu != "mips" ] ; then + echo ':mips:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mips:' > /proc/sys/fs/binfmt_misc/register + echo ':mipsel:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mipsel:' > /proc/sys/fs/binfmt_misc/register +fi diff --git a/tools/ioemu/qemu-doc.texi b/tools/ioemu/qemu-doc.texi new file mode 100644 index 0000000000..9e4735016e --- /dev/null +++ b/tools/ioemu/qemu-doc.texi @@ -0,0 +1,1748 @@ +\input texinfo @c -*- texinfo -*- +@c %**start of header +@setfilename qemu-doc.info +@settitle QEMU CPU Emulator User Documentation +@exampleindent 0 +@paragraphindent 0 +@c %**end of header + +@iftex +@titlepage +@sp 7 +@center @titlefont{QEMU CPU Emulator} +@sp 1 +@center @titlefont{User Documentation} +@sp 3 +@end titlepage +@end iftex + +@ifnottex +@node Top +@top + +@menu +* Introduction:: +* Installation:: +* QEMU PC System emulator:: +* QEMU System emulator for non PC targets:: +* QEMU Linux User space emulator:: +* compilation:: Compilation from the sources +* Index:: +@end menu +@end ifnottex + +@contents + +@node Introduction +@chapter Introduction + +@menu +* intro_features:: Features +@end menu + +@node intro_features +@section Features + +QEMU is a FAST! processor emulator using dynamic translation to +achieve good emulation speed. + +QEMU has two operating modes: + +@itemize @minus + +@item +Full system emulation. In this mode, QEMU emulates a full system (for +example a PC), including one or several processors and various +peripherals. It can be used to launch different Operating Systems +without rebooting the PC or to debug system code. + +@item +User mode emulation (Linux host only). In this mode, QEMU can launch +Linux processes compiled for one CPU on another CPU. It can be used to +launch the Wine Windows API emulator (@url{http://www.winehq.org}) or +to ease cross-compilation and cross-debugging. + +@end itemize + +QEMU can run without an host kernel driver and yet gives acceptable +performance. + +For system emulation, the following hardware targets are supported: +@itemize +@item PC (x86 or x86_64 processor) +@item ISA PC (old style PC without PCI bus) +@item PREP (PowerPC processor) +@item G3 BW PowerMac (PowerPC processor) +@item Mac99 PowerMac (PowerPC processor, in progress) +@item Sun4m (32-bit Sparc processor) +@item Sun4u (64-bit Sparc processor, in progress) +@item Malta board (32-bit MIPS processor) +@item ARM Integrator/CP (ARM926E or 1026E processor) +@end itemize + +For user emulation, x86, PowerPC, ARM, MIPS, and Sparc32/64 CPUs are supported. + +@node Installation +@chapter Installation + +If you want to compile QEMU yourself, see @ref{compilation}. + +@menu +* install_linux:: Linux +* install_windows:: Windows +* install_mac:: Macintosh +@end menu + +@node install_linux +@section Linux + +If a precompiled package is available for your distribution - you just +have to install it. Otherwise, see @ref{compilation}. + +@node install_windows +@section Windows + +Download the experimental binary installer at +@url{http://www.free.oszoo.org/@/download.html}. + +@node install_mac +@section Mac OS X + +Download the experimental binary installer at +@url{http://www.free.oszoo.org/@/download.html}. + +@node QEMU PC System emulator +@chapter QEMU PC System emulator + +@menu +* pcsys_introduction:: Introduction +* pcsys_quickstart:: Quick Start +* sec_invocation:: Invocation +* pcsys_keys:: Keys +* pcsys_monitor:: QEMU Monitor +* disk_images:: Disk Images +* pcsys_network:: Network emulation +* direct_linux_boot:: Direct Linux Boot +* pcsys_usb:: USB emulation +* gdb_usage:: GDB usage +* pcsys_os_specific:: Target OS specific information +@end menu + +@node pcsys_introduction +@section Introduction + +@c man begin DESCRIPTION + +The QEMU PC System emulator simulates the +following peripherals: + +@itemize @minus +@item +i440FX host PCI bridge and PIIX3 PCI to ISA bridge +@item +Cirrus CLGD 5446 PCI VGA card or dummy VGA card with Bochs VESA +extensions (hardware level, including all non standard modes). +@item +PS/2 mouse and keyboard +@item +2 PCI IDE interfaces with hard disk and CD-ROM support +@item +Floppy disk +@item +NE2000 PCI network adapters +@item +Serial ports +@item +Creative SoundBlaster 16 sound card +@item +ENSONIQ AudioPCI ES1370 sound card +@item +Adlib(OPL2) - Yamaha YM3812 compatible chip +@item +PCI UHCI USB controller and a virtual USB hub. +@end itemize + +SMP is supported with up to 255 CPUs. + +Note that adlib is only available when QEMU was configured with +-enable-adlib + +QEMU uses the PC BIOS from the Bochs project and the Plex86/Bochs LGPL +VGA BIOS. + +QEMU uses YM3812 emulation by Tatsuyuki Satoh. + +@c man end + +@node pcsys_quickstart +@section Quick Start + +Download and uncompress the linux image (@file{linux.img}) and type: + +@example +qemu linux.img +@end example + +Linux should boot and give you a prompt. + +@node sec_invocation +@section Invocation + +@example +@c man begin SYNOPSIS +usage: qemu [options] [disk_image] +@c man end +@end example + +@c man begin OPTIONS +@var{disk_image} is a raw hard disk image for IDE hard disk 0. + +General options: +@table @option +@item -M machine +Select the emulated machine (@code{-M ?} for list) + +@item -fda file +@item -fdb file +Use @var{file} as floppy disk 0/1 image (@pxref{disk_images}). You can +use the host floppy by using @file{/dev/fd0} as filename. + +@item -hda file +@item -hdb file +@item -hdc file +@item -hdd file +Use @var{file} as hard disk 0, 1, 2 or 3 image (@pxref{disk_images}). + +@item -cdrom file +Use @var{file} as CD-ROM image (you cannot use @option{-hdc} and and +@option{-cdrom} at the same time). You can use the host CD-ROM by +using @file{/dev/cdrom} as filename. + +@item -boot [a|c|d] +Boot on floppy (a), hard disk (c) or CD-ROM (d). Hard disk boot is +the default. + +@item -snapshot +Write to temporary files instead of disk image files. In this case, +the raw disk image you use is not written back. You can however force +the write back by pressing @key{C-a s} (@pxref{disk_images}). + +@item -m megs +Set virtual RAM size to @var{megs} megabytes. Default is 128 MB. + +@item -smp n +Simulate an SMP system with @var{n} CPUs. On the PC target, up to 255 +CPUs are supported. + +@item -nographic + +Normally, QEMU uses SDL to display the VGA output. With this option, +you can totally disable graphical output so that QEMU is a simple +command line application. The emulated serial port is redirected on +the console. Therefore, you can still use QEMU to debug a Linux kernel +with a serial console. + +@item -vnc d + +Normally, QEMU uses SDL to display the VGA output. With this option, +you can have QEMU listen on VNC display d and redirect the VGA display +over the VNC session. It is very useful to enable the usb tablet device +when using this option (option @option{-usbdevice tablet}). + +@item -k language + +Use keyboard layout @var{language} (for example @code{fr} for +French). This option is only needed where it is not easy to get raw PC +keycodes (e.g. on Macs or with some X11 servers). You don't need to +use it on PC/Linux or PC/Windows hosts. + +The available layouts are: +@example +ar de-ch es fo fr-ca hu ja mk no pt-br sv +da en-gb et fr fr-ch is lt nl pl ru th +de en-us fi fr-be hr it lv nl-be pt sl tr +@end example + +The default is @code{en-us}. + +@item -audio-help + +Will show the audio subsystem help: list of drivers, tunable +parameters. + +@item -soundhw card1,card2,... or -soundhw all + +Enable audio and selected sound hardware. Use ? to print all +available sound hardware. + +@example +qemu -soundhw sb16,adlib hda +qemu -soundhw es1370 hda +qemu -soundhw all hda +qemu -soundhw ? +@end example + +@item -localtime +Set the real time clock to local time (the default is to UTC +time). This option is needed to have correct date in MS-DOS or +Windows. + +@item -full-screen +Start in full screen. + +@item -pidfile file +Store the QEMU process PID in @var{file}. It is useful if you launch QEMU +from a script. + +@item -win2k-hack +Use it when installing Windows 2000 to avoid a disk full bug. After +Windows 2000 is installed, you no longer need this option (this option +slows down the IDE transfers). + +@end table + +USB options: +@table @option + +@item -usb +Enable the USB driver (will be the default soon) + +@item -usbdevice devname +Add the USB device @var{devname}. See the monitor command +@code{usb_add} to have more information. +@end table + +Network options: + +@table @option + +@item -net nic[,vlan=n][,macaddr=addr][,model=type] +Create a new Network Interface Card and connect it to VLAN @var{n} (@var{n} += 0 is the default). The NIC is currently an NE2000 on the PC +target. Optionally, the MAC address can be changed. If no +@option{-net} option is specified, a single NIC is created. +Qemu can emulate several different models of network card. Valid values for +@var{type} are @code{ne2k_pci}, @code{ne2k_isa}, @code{rtl8139}, +@code{smc91c111} and @code{lance}. Not all devices are supported on all +targets. + +@item -net user[,vlan=n][,hostname=name] +Use the user mode network stack which requires no administrator +priviledge to run. @option{hostname=name} can be used to specify the client +hostname reported by the builtin DHCP server. + +@item -net tap[,vlan=n][,fd=h][,ifname=name][,script=file] +Connect the host TAP network interface @var{name} to VLAN @var{n} and +use the network script @var{file} to configure it. The default +network script is @file{/etc/qemu-ifup}. If @var{name} is not +provided, the OS automatically provides one. @option{fd=h} can be +used to specify the handle of an already opened host TAP interface. Example: + +@example +qemu linux.img -net nic -net tap +@end example + +More complicated example (two NICs, each one connected to a TAP device) +@example +qemu linux.img -net nic,vlan=0 -net tap,vlan=0,ifname=tap0 \ + -net nic,vlan=1 -net tap,vlan=1,ifname=tap1 +@end example + + +@item -net socket[,vlan=n][,fd=h][,listen=[host]:port][,connect=host:port] + +Connect the VLAN @var{n} to a remote VLAN in another QEMU virtual +machine using a TCP socket connection. If @option{listen} is +specified, QEMU waits for incoming connections on @var{port} +(@var{host} is optional). @option{connect} is used to connect to +another QEMU instance using the @option{listen} option. @option{fd=h} +specifies an already opened TCP socket. + +Example: +@example +# launch a first QEMU instance +qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \ + -net socket,listen=:1234 +# connect the VLAN 0 of this instance to the VLAN 0 +# of the first instance +qemu linux.img -net nic,macaddr=52:54:00:12:34:57 \ + -net socket,connect=127.0.0.1:1234 +@end example + +@item -net socket[,vlan=n][,fd=h][,mcast=maddr:port] + +Create a VLAN @var{n} shared with another QEMU virtual +machines using a UDP multicast socket, effectively making a bus for +every QEMU with same multicast address @var{maddr} and @var{port}. +NOTES: +@enumerate +@item +Several QEMU can be running on different hosts and share same bus (assuming +correct multicast setup for these hosts). +@item +mcast support is compatible with User Mode Linux (argument @option{eth@var{N}=mcast}), see +@url{http://user-mode-linux.sf.net}. +@item Use @option{fd=h} to specify an already opened UDP multicast socket. +@end enumerate + +Example: +@example +# launch one QEMU instance +qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \ + -net socket,mcast=230.0.0.1:1234 +# launch another QEMU instance on same "bus" +qemu linux.img -net nic,macaddr=52:54:00:12:34:57 \ + -net socket,mcast=230.0.0.1:1234 +# launch yet another QEMU instance on same "bus" +qemu linux.img -net nic,macaddr=52:54:00:12:34:58 \ + -net socket,mcast=230.0.0.1:1234 +@end example + +Example (User Mode Linux compat.): +@example +# launch QEMU instance (note mcast address selected +# is UML's default) +qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \ + -net socket,mcast=239.192.168.1:1102 +# launch UML +/path/to/linux ubd0=/path/to/root_fs eth0=mcast +@end example + +@item -net none +Indicate that no network devices should be configured. It is used to +override the default configuration (@option{-net nic -net user}) which +is activated if no @option{-net} options are provided. + +@item -tftp prefix +When using the user mode network stack, activate a built-in TFTP +server. All filenames beginning with @var{prefix} can be downloaded +from the host to the guest using a TFTP client. The TFTP client on the +guest must be configured in binary mode (use the command @code{bin} of +the Unix TFTP client). The host IP address on the guest is as usual +10.0.2.2. + +@item -smb dir +When using the user mode network stack, activate a built-in SMB +server so that Windows OSes can access to the host files in @file{dir} +transparently. + +In the guest Windows OS, the line: +@example +10.0.2.4 smbserver +@end example +must be added in the file @file{C:\WINDOWS\LMHOSTS} (for windows 9x/Me) +or @file{C:\WINNT\SYSTEM32\DRIVERS\ETC\LMHOSTS} (Windows NT/2000). + +Then @file{dir} can be accessed in @file{\\smbserver\qemu}. + +Note that a SAMBA server must be installed on the host OS in +@file{/usr/sbin/smbd}. QEMU was tested succesfully with smbd version +2.2.7a from the Red Hat 9 and version 3.0.10-1.fc3 from Fedora Core 3. + +@item -redir [tcp|udp]:host-port:[guest-host]:guest-port + +When using the user mode network stack, redirect incoming TCP or UDP +connections to the host port @var{host-port} to the guest +@var{guest-host} on guest port @var{guest-port}. If @var{guest-host} +is not specified, its value is 10.0.2.15 (default address given by the +built-in DHCP server). + +For example, to redirect host X11 connection from screen 1 to guest +screen 0, use the following: + +@example +# on the host +qemu -redir tcp:6001::6000 [...] +# this host xterm should open in the guest X11 server +xterm -display :1 +@end example + +To redirect telnet connections from host port 5555 to telnet port on +the guest, use the following: + +@example +# on the host +qemu -redir tcp:5555::23 [...] +telnet localhost 5555 +@end example + +Then when you use on the host @code{telnet localhost 5555}, you +connect to the guest telnet server. + +@end table + +Linux boot specific: When using these options, you can use a given +Linux kernel without installing it in the disk image. It can be useful +for easier testing of various kernels. + +@table @option + +@item -kernel bzImage +Use @var{bzImage} as kernel image. + +@item -append cmdline +Use @var{cmdline} as kernel command line + +@item -initrd file +Use @var{file} as initial ram disk. + +@end table + +Debug/Expert options: +@table @option + +@item -serial dev +Redirect the virtual serial port to host device @var{dev}. Available +devices are: +@table @code +@item vc +Virtual console +@item pty +[Linux only] Pseudo TTY (a new PTY is automatically allocated) +@item null +void device +@item /dev/XXX +[Linux only] Use host tty, e.g. @file{/dev/ttyS0}. The host serial port +parameters are set according to the emulated ones. +@item /dev/parportN +[Linux only, parallel port only] Use host parallel port +@var{N}. Currently only SPP parallel port features can be used. +@item file:filename +Write output to filename. No character can be read. +@item stdio +[Unix only] standard input/output +@item pipe:filename +[Unix only] name pipe @var{filename} +@end table +The default device is @code{vc} in graphical mode and @code{stdio} in +non graphical mode. + +This option can be used several times to simulate up to 4 serials +ports. + +@item -parallel dev +Redirect the virtual parallel port to host device @var{dev} (same +devices as the serial port). On Linux hosts, @file{/dev/parportN} can +be used to use hardware devices connected on the corresponding host +parallel port. + +This option can be used several times to simulate up to 3 parallel +ports. + +@item -monitor dev +Redirect the monitor to host device @var{dev} (same devices as the +serial port). +The default device is @code{vc} in graphical mode and @code{stdio} in +non graphical mode. + +@item -s +Wait gdb connection to port 1234 (@pxref{gdb_usage}). +@item -p port +Change gdb connection port. +@item -S +Do not start CPU at startup (you must type 'c' in the monitor). +@item -d +Output log in /tmp/qemu.log +@item -hdachs c,h,s,[,t] +Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <= +@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS +translation mode (@var{t}=none, lba or auto). Usually QEMU can guess +all thoses parameters. This option is useful for old MS-DOS disk +images. + +@item -std-vga +Simulate a standard VGA card with Bochs VBE extensions (default is +Cirrus Logic GD5446 PCI VGA) +@item -loadvm file +Start right away with a saved state (@code{loadvm} in monitor) +@end table + +@c man end + +@node pcsys_keys +@section Keys + +@c man begin OPTIONS + +During the graphical emulation, you can use the following keys: +@table @key +@item Ctrl-Alt-f +Toggle full screen + +@item Ctrl-Alt-n +Switch to virtual console 'n'. Standard console mappings are: +@table @emph +@item 1 +Target system display +@item 2 +Monitor +@item 3 +Serial port +@end table + +@item Ctrl-Alt +Toggle mouse and keyboard grab. +@end table + +In the virtual consoles, you can use @key{Ctrl-Up}, @key{Ctrl-Down}, +@key{Ctrl-PageUp} and @key{Ctrl-PageDown} to move in the back log. + +During emulation, if you are using the @option{-nographic} option, use +@key{Ctrl-a h} to get terminal commands: + +@table @key +@item Ctrl-a h +Print this help +@item Ctrl-a x +Exit emulatior +@item Ctrl-a s +Save disk data back to file (if -snapshot) +@item Ctrl-a b +Send break (magic sysrq in Linux) +@item Ctrl-a c +Switch between console and monitor +@item Ctrl-a Ctrl-a +Send Ctrl-a +@end table +@c man end + +@ignore + +@c man begin SEEALSO +The HTML documentation of QEMU for more precise information and Linux +user mode emulator invocation. +@c man end + +@c man begin AUTHOR +Fabrice Bellard +@c man end + +@end ignore + +@node pcsys_monitor +@section QEMU Monitor + +The QEMU monitor is used to give complex commands to the QEMU +emulator. You can use it to: + +@itemize @minus + +@item +Remove or insert removable medias images +(such as CD-ROM or floppies) + +@item +Freeze/unfreeze the Virtual Machine (VM) and save or restore its state +from a disk file. + +@item Inspect the VM state without an external debugger. + +@end itemize + +@subsection Commands + +The following commands are available: + +@table @option + +@item help or ? [cmd] +Show the help for all commands or just for command @var{cmd}. + +@item commit +Commit changes to the disk images (if -snapshot is used) + +@item info subcommand +show various information about the system state + +@table @option +@item info network +show the various VLANs and the associated devices +@item info block +show the block devices +@item info registers +show the cpu registers +@item info history +show the command line history +@item info pci +show emulated PCI device +@item info usb +show USB devices plugged on the virtual USB hub +@item info usbhost +show all USB host devices +@end table + +@item q or quit +Quit the emulator. + +@item eject [-f] device +Eject a removable media (use -f to force it). + +@item change device filename +Change a removable media. + +@item screendump filename +Save screen into PPM image @var{filename}. + +@item log item1[,...] +Activate logging of the specified items to @file{/tmp/qemu.log}. + +@item savevm filename +Save the whole virtual machine state to @var{filename}. + +@item loadvm filename +Restore the whole virtual machine state from @var{filename}. + +@item stop +Stop emulation. + +@item c or cont +Resume emulation. + +@item gdbserver [port] +Start gdbserver session (default port=1234) + +@item x/fmt addr +Virtual memory dump starting at @var{addr}. + +@item xp /fmt addr +Physical memory dump starting at @var{addr}. + +@var{fmt} is a format which tells the command how to format the +data. Its syntax is: @option{/@{count@}@{format@}@{size@}} + +@table @var +@item count +is the number of items to be dumped. + +@item format +can be x (hexa), d (signed decimal), u (unsigned decimal), o (octal), +c (char) or i (asm instruction). + +@item size +can be b (8 bits), h (16 bits), w (32 bits) or g (64 bits). On x86, +@code{h} or @code{w} can be specified with the @code{i} format to +respectively select 16 or 32 bit code instruction size. + +@end table + +Examples: +@itemize +@item +Dump 10 instructions at the current instruction pointer: +@example +(qemu) x/10i $eip +0x90107063: ret +0x90107064: sti +0x90107065: lea 0x0(%esi,1),%esi +0x90107069: lea 0x0(%edi,1),%edi +0x90107070: ret +0x90107071: jmp 0x90107080 +0x90107073: nop +0x90107074: nop +0x90107075: nop +0x90107076: nop +@end example + +@item +Dump 80 16 bit values at the start of the video memory. +@smallexample +(qemu) xp/80hx 0xb8000 +0x000b8000: 0x0b50 0x0b6c 0x0b65 0x0b78 0x0b38 0x0b36 0x0b2f 0x0b42 +0x000b8010: 0x0b6f 0x0b63 0x0b68 0x0b73 0x0b20 0x0b56 0x0b47 0x0b41 +0x000b8020: 0x0b42 0x0b69 0x0b6f 0x0b73 0x0b20 0x0b63 0x0b75 0x0b72 +0x000b8030: 0x0b72 0x0b65 0x0b6e 0x0b74 0x0b2d 0x0b63 0x0b76 0x0b73 +0x000b8040: 0x0b20 0x0b30 0x0b35 0x0b20 0x0b4e 0x0b6f 0x0b76 0x0b20 +0x000b8050: 0x0b32 0x0b30 0x0b30 0x0b33 0x0720 0x0720 0x0720 0x0720 +0x000b8060: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 +0x000b8070: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 +0x000b8080: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 +0x000b8090: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 +@end smallexample +@end itemize + +@item p or print/fmt expr + +Print expression value. Only the @var{format} part of @var{fmt} is +used. + +@item sendkey keys + +Send @var{keys} to the emulator. Use @code{-} to press several keys +simultaneously. Example: +@example +sendkey ctrl-alt-f1 +@end example + +This command is useful to send keys that your graphical user interface +intercepts at low level, such as @code{ctrl-alt-f1} in X Window. + +@item system_reset + +Reset the system. + +@item usb_add devname + +Plug the USB device devname to the QEMU virtual USB hub. @var{devname} +is either a virtual device name (for example @code{mouse}) or a host +USB device identifier. Host USB device identifiers have the following +syntax: @code{host:bus.addr} or @code{host:vendor_id:product_id}. + +@item usb_del devname + +Remove the USB device @var{devname} from the QEMU virtual USB +hub. @var{devname} has the syntax @code{bus.addr}. Use the monitor +command @code{info usb} to see the devices you can remove. + +@end table + +@subsection Integer expressions + +The monitor understands integers expressions for every integer +argument. You can use register names to get the value of specifics +CPU registers by prefixing them with @emph{$}. + +@node disk_images +@section Disk Images + +Since version 0.6.1, QEMU supports many disk image formats, including +growable disk images (their size increase as non empty sectors are +written), compressed and encrypted disk images. + +@menu +* disk_images_quickstart:: Quick start for disk image creation +* disk_images_snapshot_mode:: Snapshot mode +* qemu_img_invocation:: qemu-img Invocation +* disk_images_fat_images:: Virtual FAT disk images +@end menu + +@node disk_images_quickstart +@subsection Quick start for disk image creation + +You can create a disk image with the command: +@example +qemu-img create myimage.img mysize +@end example +where @var{myimage.img} is the disk image filename and @var{mysize} is its +size in kilobytes. You can add an @code{M} suffix to give the size in +megabytes and a @code{G} suffix for gigabytes. + +See @ref{qemu_img_invocation} for more information. + +@node disk_images_snapshot_mode +@subsection Snapshot mode + +If you use the option @option{-snapshot}, all disk images are +considered as read only. When sectors in written, they are written in +a temporary file created in @file{/tmp}. You can however force the +write back to the raw disk images by using the @code{commit} monitor +command (or @key{C-a s} in the serial console). + +@node qemu_img_invocation +@subsection @code{qemu-img} Invocation + +@include qemu-img.texi + +@node disk_images_fat_images +@subsection Virtual FAT disk images + +QEMU can automatically create a virtual FAT disk image from a +directory tree. In order to use it, just type: + +@example +qemu linux.img -hdb fat:/my_directory +@end example + +Then you access access to all the files in the @file{/my_directory} +directory without having to copy them in a disk image or to export +them via SAMBA or NFS. The default access is @emph{read-only}. + +Floppies can be emulated with the @code{:floppy:} option: + +@example +qemu linux.img -fda fat:floppy:/my_directory +@end example + +A read/write support is available for testing (beta stage) with the +@code{:rw:} option: + +@example +qemu linux.img -fda fat:floppy:rw:/my_directory +@end example + +What you should @emph{never} do: +@itemize +@item use non-ASCII filenames ; +@item use "-snapshot" together with ":rw:" ; +@item expect it to work when loadvm'ing ; +@item write to the FAT directory on the host system while accessing it with the guest system. +@end itemize + +@node pcsys_network +@section Network emulation + +QEMU can simulate several networks cards (NE2000 boards on the PC +target) and can connect them to an arbitrary number of Virtual Local +Area Networks (VLANs). Host TAP devices can be connected to any QEMU +VLAN. VLAN can be connected between separate instances of QEMU to +simulate large networks. For simpler usage, a non priviledged user mode +network stack can replace the TAP device to have a basic network +connection. + +@subsection VLANs + +QEMU simulates several VLANs. A VLAN can be symbolised as a virtual +connection between several network devices. These devices can be for +example QEMU virtual Ethernet cards or virtual Host ethernet devices +(TAP devices). + +@subsection Using TAP network interfaces + +This is the standard way to connect QEMU to a real network. QEMU adds +a virtual network device on your host (called @code{tapN}), and you +can then configure it as if it was a real ethernet card. + +As an example, you can download the @file{linux-test-xxx.tar.gz} +archive and copy the script @file{qemu-ifup} in @file{/etc} and +configure properly @code{sudo} so that the command @code{ifconfig} +contained in @file{qemu-ifup} can be executed as root. You must verify +that your host kernel supports the TAP network interfaces: the +device @file{/dev/net/tun} must be present. + +See @ref{direct_linux_boot} to have an example of network use with a +Linux distribution and @ref{sec_invocation} to have examples of +command lines using the TAP network interfaces. + +@subsection Using the user mode network stack + +By using the option @option{-net user} (default configuration if no +@option{-net} option is specified), QEMU uses a completely user mode +network stack (you don't need root priviledge to use the virtual +network). The virtual network configuration is the following: + +@example + + QEMU VLAN <------> Firewall/DHCP server <-----> Internet + | (10.0.2.2) + | + ----> DNS server (10.0.2.3) + | + ----> SMB server (10.0.2.4) +@end example + +The QEMU VM behaves as if it was behind a firewall which blocks all +incoming connections. You can use a DHCP client to automatically +configure the network in the QEMU VM. The DHCP server assign addresses +to the hosts starting from 10.0.2.15. + +In order to check that the user mode network is working, you can ping +the address 10.0.2.2 and verify that you got an address in the range +10.0.2.x from the QEMU virtual DHCP server. + +Note that @code{ping} is not supported reliably to the internet as it +would require root priviledges. It means you can only ping the local +router (10.0.2.2). + +When using the built-in TFTP server, the router is also the TFTP +server. + +When using the @option{-redir} option, TCP or UDP connections can be +redirected from the host to the guest. It allows for example to +redirect X11, telnet or SSH connections. + +@subsection Connecting VLANs between QEMU instances + +Using the @option{-net socket} option, it is possible to make VLANs +that span several QEMU instances. See @ref{sec_invocation} to have a +basic example. + +@node direct_linux_boot +@section Direct Linux Boot + +This section explains how to launch a Linux kernel inside QEMU without +having to make a full bootable image. It is very useful for fast Linux +kernel testing. The QEMU network configuration is also explained. + +@enumerate +@item +Download the archive @file{linux-test-xxx.tar.gz} containing a Linux +kernel and a disk image. + +@item Optional: If you want network support (for example to launch X11 examples), you +must copy the script @file{qemu-ifup} in @file{/etc} and configure +properly @code{sudo} so that the command @code{ifconfig} contained in +@file{qemu-ifup} can be executed as root. You must verify that your host +kernel supports the TUN/TAP network interfaces: the device +@file{/dev/net/tun} must be present. + +When network is enabled, there is a virtual network connection between +the host kernel and the emulated kernel. The emulated kernel is seen +from the host kernel at IP address 172.20.0.2 and the host kernel is +seen from the emulated kernel at IP address 172.20.0.1. + +@item Launch @code{qemu.sh}. You should have the following output: + +@smallexample +> ./qemu.sh +Connected to host network interface: tun0 +Linux version 2.4.21 (bellard@@voyager.localdomain) (gcc version 3.2.2 20030222 @/(Red Hat @/Linux 3.2.2-5)) #5 Tue Nov 11 18:18:53 CET 2003 +BIOS-provided physical RAM map: + BIOS-e801: 0000000000000000 - 000000000009f000 (usable) + BIOS-e801: 0000000000100000 - 0000000002000000 (usable) +32MB LOWMEM available. +On node 0 totalpages: 8192 +zone(0): 4096 pages. +zone(1): 4096 pages. +zone(2): 0 pages. +Kernel command line: root=/dev/hda sb=0x220,5,1,5 ide2=noprobe ide3=noprobe ide4=noprobe @/ide5=noprobe console=ttyS0 +ide_setup: ide2=noprobe +ide_setup: ide3=noprobe +ide_setup: ide4=noprobe +ide_setup: ide5=noprobe +Initializing CPU#0 +Detected 2399.621 MHz processor. +Console: colour EGA 80x25 +Calibrating delay loop... 4744.80 BogoMIPS +Memory: 28872k/32768k available (1210k kernel code, 3508k reserved, 266k data, 64k init, @/0k highmem) +Dentry cache hash table entries: 4096 (order: 3, 32768 bytes) +Inode cache hash table entries: 2048 (order: 2, 16384 bytes) +Mount cache hash table entries: 512 (order: 0, 4096 bytes) +Buffer-cache hash table entries: 1024 (order: 0, 4096 bytes) +Page-cache hash table entries: 8192 (order: 3, 32768 bytes) +CPU: Intel Pentium Pro stepping 03 +Checking 'hlt' instruction... OK. +POSIX conformance testing by UNIFIX +Linux NET4.0 for Linux 2.4 +Based upon Swansea University Computer Society NET3.039 +Initializing RT netlink socket +apm: BIOS not found. +Starting kswapd +Journalled Block Device driver loaded +Detected PS/2 Mouse Port. +pty: 256 Unix98 ptys configured +Serial driver version 5.05c (2001-07-08) with no serial options enabled +ttyS00 at 0x03f8 (irq = 4) is a 16450 +ne.c:v1.10 9/23/94 Donald Becker (becker@@scyld.com) +Last modified Nov 1, 2000 by Paul Gortmaker +NE*000 ethercard probe at 0x300: 52 54 00 12 34 56 +eth0: NE2000 found at 0x300, using IRQ 9. +RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize +Uniform Multi-Platform E-IDE driver Revision: 7.00beta4-2.4 +ide: Assuming 50MHz system bus speed for PIO modes; override with idebus=xx +hda: QEMU HARDDISK, ATA DISK drive +ide0 at 0x1f0-0x1f7,0x3f6 on irq 14 +hda: attached ide-disk driver. +hda: 20480 sectors (10 MB) w/256KiB Cache, CHS=20/16/63 +Partition check: + hda: +Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996 +NET4: Linux TCP/IP 1.0 for NET4.0 +IP Protocols: ICMP, UDP, TCP, IGMP +IP: routing cache hash table of 512 buckets, 4Kbytes +TCP: Hash tables configured (established 2048 bind 4096) +NET4: Unix domain sockets 1.0/SMP for Linux NET4.0. +EXT2-fs warning: mounting unchecked fs, running e2fsck is recommended +VFS: Mounted root (ext2 filesystem). +Freeing unused kernel memory: 64k freed + +Linux version 2.4.21 (bellard@@voyager.localdomain) (gcc version 3.2.2 20030222 @/(Red Hat @/Linux 3.2.2-5)) #5 Tue Nov 11 18:18:53 CET 2003 + +QEMU Linux test distribution (based on Redhat 9) + +Type 'exit' to halt the system + +sh-2.05b# +@end smallexample + +@item +Then you can play with the kernel inside the virtual serial console. You +can launch @code{ls} for example. Type @key{Ctrl-a h} to have an help +about the keys you can type inside the virtual serial console. In +particular, use @key{Ctrl-a x} to exit QEMU and use @key{Ctrl-a b} as +the Magic SysRq key. + +@item +If the network is enabled, launch the script @file{/etc/linuxrc} in the +emulator (don't forget the leading dot): +@example +. /etc/linuxrc +@end example + +Then enable X11 connections on your PC from the emulated Linux: +@example +xhost +172.20.0.2 +@end example + +You can now launch @file{xterm} or @file{xlogo} and verify that you have +a real Virtual Linux system ! + +@end enumerate + +NOTES: +@enumerate +@item +A 2.5.74 kernel is also included in the archive. Just +replace the bzImage in qemu.sh to try it. + +@item +In order to exit cleanly from qemu, you can do a @emph{shutdown} inside +qemu. qemu will automatically exit when the Linux shutdown is done. + +@item +You can boot slightly faster by disabling the probe of non present IDE +interfaces. To do so, add the following options on the kernel command +line: +@example +ide1=noprobe ide2=noprobe ide3=noprobe ide4=noprobe ide5=noprobe +@end example + +@item +The example disk image is a modified version of the one made by Kevin +Lawton for the plex86 Project (@url{www.plex86.org}). + +@end enumerate + +@node pcsys_usb +@section USB emulation + +QEMU emulates a PCI UHCI USB controller and a 8 port USB hub connected +to it. You can virtually plug to the hub virtual USB devices or real +host USB devices (experimental, works only on Linux hosts). + +@subsection Using virtual USB devices + +A virtual USB mouse device is available for testing in QEMU. + +You can try it with the following monitor commands: + +@example +# add the mouse device +(qemu) usb_add mouse + +# show the virtual USB devices plugged on the QEMU Virtual USB hub +(qemu) info usb + Device 0.3, speed 12 Mb/s + +# after some time you can try to remove the mouse +(qemu) usb_del 0.3 +@end example + +The option @option{-usbdevice} is similar to the monitor command +@code{usb_add}. + +@subsection Using host USB devices on a Linux host + +WARNING: this is an experimental feature. QEMU will slow down when +using it. USB devices requiring real time streaming (i.e. USB Video +Cameras) are not supported yet. + +@enumerate +@item If you use an early Linux 2.4 kernel, verify that no Linux driver +is actually using the USB device. A simple way to do that is simply to +disable the corresponding kernel module by renaming it from @file{mydriver.o} +to @file{mydriver.o.disabled}. + +@item Verify that @file{/proc/bus/usb} is working (most Linux distributions should enable it by default). You should see something like that: +@example +ls /proc/bus/usb +001 devices drivers +@end example + +@item Since only root can access to the USB devices directly, you can either launch QEMU as root or change the permissions of the USB devices you want to use. For testing, the following suffices: +@example +chown -R myuid /proc/bus/usb +@end example + +@item Launch QEMU and do in the monitor: +@example +info usbhost + Device 1.2, speed 480 Mb/s + Class 00: USB device 1234:5678, USB DISK +@end example +You should see the list of the devices you can use (Never try to use +hubs, it won't work). + +@item Add the device in QEMU by using: +@example +usb_add host:1234:5678 +@end example + +Normally the guest OS should report that a new USB device is +plugged. You can use the option @option{-usbdevice} to do the same. + +@item Now you can try to use the host USB device in QEMU. + +@end enumerate + +When relaunching QEMU, you may have to unplug and plug again the USB +device to make it work again (this is a bug). + +@node gdb_usage +@section GDB usage + +QEMU has a primitive support to work with gdb, so that you can do +'Ctrl-C' while the virtual machine is running and inspect its state. + +In order to use gdb, launch qemu with the '-s' option. It will wait for a +gdb connection: +@example +> qemu -s -kernel arch/i386/boot/bzImage -hda root-2.4.20.img \ + -append "root=/dev/hda" +Connected to host network interface: tun0 +Waiting gdb connection on port 1234 +@end example + +Then launch gdb on the 'vmlinux' executable: +@example +> gdb vmlinux +@end example + +In gdb, connect to QEMU: +@example +(gdb) target remote localhost:1234 +@end example + +Then you can use gdb normally. For example, type 'c' to launch the kernel: +@example +(gdb) c +@end example + +Here are some useful tips in order to use gdb on system code: + +@enumerate +@item +Use @code{info reg} to display all the CPU registers. +@item +Use @code{x/10i $eip} to display the code at the PC position. +@item +Use @code{set architecture i8086} to dump 16 bit code. Then use +@code{x/10i $cs*16+*eip} to dump the code at the PC position. +@end enumerate + +@node pcsys_os_specific +@section Target OS specific information + +@subsection Linux + +To have access to SVGA graphic modes under X11, use the @code{vesa} or +the @code{cirrus} X11 driver. For optimal performances, use 16 bit +color depth in the guest and the host OS. + +When using a 2.6 guest Linux kernel, you should add the option +@code{clock=pit} on the kernel command line because the 2.6 Linux +kernels make very strict real time clock checks by default that QEMU +cannot simulate exactly. + +When using a 2.6 guest Linux kernel, verify that the 4G/4G patch is +not activated because QEMU is slower with this patch. The QEMU +Accelerator Module is also much slower in this case. Earlier Fedora +Core 3 Linux kernel (< 2.6.9-1.724_FC3) were known to incorporte this +patch by default. Newer kernels don't have it. + +@subsection Windows + +If you have a slow host, using Windows 95 is better as it gives the +best speed. Windows 2000 is also a good choice. + +@subsubsection SVGA graphic modes support + +QEMU emulates a Cirrus Logic GD5446 Video +card. All Windows versions starting from Windows 95 should recognize +and use this graphic card. For optimal performances, use 16 bit color +depth in the guest and the host OS. + +@subsubsection CPU usage reduction + +Windows 9x does not correctly use the CPU HLT +instruction. The result is that it takes host CPU cycles even when +idle. You can install the utility from +@url{http://www.user.cityline.ru/~maxamn/amnhltm.zip} to solve this +problem. Note that no such tool is needed for NT, 2000 or XP. + +@subsubsection Windows 2000 disk full problem + +Windows 2000 has a bug which gives a disk full problem during its +installation. When installing it, use the @option{-win2k-hack} QEMU +option to enable a specific workaround. After Windows 2000 is +installed, you no longer need this option (this option slows down the +IDE transfers). + +@subsubsection Windows 2000 shutdown + +Windows 2000 cannot automatically shutdown in QEMU although Windows 98 +can. It comes from the fact that Windows 2000 does not automatically +use the APM driver provided by the BIOS. + +In order to correct that, do the following (thanks to Struan +Bartlett): go to the Control Panel => Add/Remove Hardware & Next => +Add/Troubleshoot a device => Add a new device & Next => No, select the +hardware from a list & Next => NT Apm/Legacy Support & Next => Next +(again) a few times. Now the driver is installed and Windows 2000 now +correctly instructs QEMU to shutdown at the appropriate moment. + +@subsubsection Share a directory between Unix and Windows + +See @ref{sec_invocation} about the help of the option @option{-smb}. + +@subsubsection Windows XP security problems + +Some releases of Windows XP install correctly but give a security +error when booting: +@example +A problem is preventing Windows from accurately checking the +license for this computer. Error code: 0x800703e6. +@end example +The only known workaround is to boot in Safe mode +without networking support. + +Future QEMU releases are likely to correct this bug. + +@subsection MS-DOS and FreeDOS + +@subsubsection CPU usage reduction + +DOS does not correctly use the CPU HLT instruction. The result is that +it takes host CPU cycles even when idle. You can install the utility +from @url{http://www.vmware.com/software/dosidle210.zip} to solve this +problem. + +@node QEMU System emulator for non PC targets +@chapter QEMU System emulator for non PC targets + +QEMU is a generic emulator and it emulates many non PC +machines. Most of the options are similar to the PC emulator. The +differences are mentionned in the following sections. + +@menu +* QEMU PowerPC System emulator:: +* Sparc32 System emulator invocation:: +* Sparc64 System emulator invocation:: +* MIPS System emulator invocation:: +* ARM System emulator invocation:: +@end menu + +@node QEMU PowerPC System emulator +@section QEMU PowerPC System emulator + +Use the executable @file{qemu-system-ppc} to simulate a complete PREP +or PowerMac PowerPC system. + +QEMU emulates the following PowerMac peripherals: + +@itemize @minus +@item +UniNorth PCI Bridge +@item +PCI VGA compatible card with VESA Bochs Extensions +@item +2 PMAC IDE interfaces with hard disk and CD-ROM support +@item +NE2000 PCI adapters +@item +Non Volatile RAM +@item +VIA-CUDA with ADB keyboard and mouse. +@end itemize + +QEMU emulates the following PREP peripherals: + +@itemize @minus +@item +PCI Bridge +@item +PCI VGA compatible card with VESA Bochs Extensions +@item +2 IDE interfaces with hard disk and CD-ROM support +@item +Floppy disk +@item +NE2000 network adapters +@item +Serial port +@item +PREP Non Volatile RAM +@item +PC compatible keyboard and mouse. +@end itemize + +QEMU uses the Open Hack'Ware Open Firmware Compatible BIOS available at +@url{http://perso.magic.fr/l_indien/OpenHackWare/index.htm}. + +@c man begin OPTIONS + +The following options are specific to the PowerPC emulation: + +@table @option + +@item -g WxH[xDEPTH] + +Set the initial VGA graphic mode. The default is 800x600x15. + +@end table + +@c man end + + +More information is available at +@url{http://perso.magic.fr/l_indien/qemu-ppc/}. + +@node Sparc32 System emulator invocation +@section Sparc32 System emulator invocation + +Use the executable @file{qemu-system-sparc} to simulate a JavaStation +(sun4m architecture). The emulation is somewhat complete. + +QEMU emulates the following sun4m peripherals: + +@itemize @minus +@item +IOMMU +@item +TCX Frame buffer +@item +Lance (Am7990) Ethernet +@item +Non Volatile RAM M48T08 +@item +Slave I/O: timers, interrupt controllers, Zilog serial ports, keyboard +and power/reset logic +@item +ESP SCSI controller with hard disk and CD-ROM support +@item +Floppy drive +@end itemize + +The number of peripherals is fixed in the architecture. + +QEMU uses the Proll, a PROM replacement available at +@url{http://people.redhat.com/@/zaitcev/linux/}. The required +QEMU-specific patches are included with the sources. + +A sample Linux 2.6 series kernel and ram disk image are available on +the QEMU web site. Please note that currently neither Linux 2.4 +series, NetBSD, nor OpenBSD kernels work. + +@c man begin OPTIONS + +The following options are specific to the Sparc emulation: + +@table @option + +@item -g WxH + +Set the initial TCX graphic mode. The default is 1024x768. + +@end table + +@c man end + +@node Sparc64 System emulator invocation +@section Sparc64 System emulator invocation + +Use the executable @file{qemu-system-sparc64} to simulate a Sun4u machine. +The emulator is not usable for anything yet. + +QEMU emulates the following sun4u peripherals: + +@itemize @minus +@item +UltraSparc IIi APB PCI Bridge +@item +PCI VGA compatible card with VESA Bochs Extensions +@item +Non Volatile RAM M48T59 +@item +PC-compatible serial ports +@end itemize + +@node MIPS System emulator invocation +@section MIPS System emulator invocation + +Use the executable @file{qemu-system-mips} to simulate a MIPS machine. +The emulator is able to boot a Linux kernel and to run a Linux Debian +installation from NFS. The following devices are emulated: + +@itemize @minus +@item +MIPS R4K CPU +@item +PC style serial port +@item +NE2000 network card +@end itemize + +More information is available in the QEMU mailing-list archive. + +@node ARM System emulator invocation +@section ARM System emulator invocation + +Use the executable @file{qemu-system-arm} to simulate a ARM +machine. The ARM Integrator/CP board is emulated with the following +devices: + +@itemize @minus +@item +ARM926E or ARM1026E CPU +@item +Two PL011 UARTs +@item +SMC 91c111 Ethernet adapter +@end itemize + +A Linux 2.6 test image is available on the QEMU web site. More +information is available in the QEMU mailing-list archive. + +@node QEMU Linux User space emulator +@chapter QEMU Linux User space emulator + +@menu +* Quick Start:: +* Wine launch:: +* Command line options:: +@end menu + +@node Quick Start +@section Quick Start + +In order to launch a Linux process, QEMU needs the process executable +itself and all the target (x86) dynamic libraries used by it. + +@itemize + +@item On x86, you can just try to launch any process by using the native +libraries: + +@example +qemu-i386 -L / /bin/ls +@end example + +@code{-L /} tells that the x86 dynamic linker must be searched with a +@file{/} prefix. + +@item Since QEMU is also a linux process, you can launch qemu with qemu (NOTE: you can only do that if you compiled QEMU from the sources): + +@example +qemu-i386 -L / qemu-i386 -L / /bin/ls +@end example + +@item On non x86 CPUs, you need first to download at least an x86 glibc +(@file{qemu-runtime-i386-XXX-.tar.gz} on the QEMU web page). Ensure that +@code{LD_LIBRARY_PATH} is not set: + +@example +unset LD_LIBRARY_PATH +@end example + +Then you can launch the precompiled @file{ls} x86 executable: + +@example +qemu-i386 tests/i386/ls +@end example +You can look at @file{qemu-binfmt-conf.sh} so that +QEMU is automatically launched by the Linux kernel when you try to +launch x86 executables. It requires the @code{binfmt_misc} module in the +Linux kernel. + +@item The x86 version of QEMU is also included. You can try weird things such as: +@example +qemu-i386 /usr/local/qemu-i386/bin/qemu-i386 \ + /usr/local/qemu-i386/bin/ls-i386 +@end example + +@end itemize + +@node Wine launch +@section Wine launch + +@itemize + +@item Ensure that you have a working QEMU with the x86 glibc +distribution (see previous section). In order to verify it, you must be +able to do: + +@example +qemu-i386 /usr/local/qemu-i386/bin/ls-i386 +@end example + +@item Download the binary x86 Wine install +(@file{qemu-XXX-i386-wine.tar.gz} on the QEMU web page). + +@item Configure Wine on your account. Look at the provided script +@file{/usr/local/qemu-i386/@/bin/wine-conf.sh}. Your previous +@code{$@{HOME@}/.wine} directory is saved to @code{$@{HOME@}/.wine.org}. + +@item Then you can try the example @file{putty.exe}: + +@example +qemu-i386 /usr/local/qemu-i386/wine/bin/wine \ + /usr/local/qemu-i386/wine/c/Program\ Files/putty.exe +@end example + +@end itemize + +@node Command line options +@section Command line options + +@example +usage: qemu-i386 [-h] [-d] [-L path] [-s size] program [arguments...] +@end example + +@table @option +@item -h +Print the help +@item -L path +Set the x86 elf interpreter prefix (default=/usr/local/qemu-i386) +@item -s size +Set the x86 stack size in bytes (default=524288) +@end table + +Debug options: + +@table @option +@item -d +Activate log (logfile=/tmp/qemu.log) +@item -p pagesize +Act as if the host page size was 'pagesize' bytes +@end table + +@node compilation +@chapter Compilation from the sources + +@menu +* Linux/Unix:: +* Windows:: +* Cross compilation for Windows with Linux:: +* Mac OS X:: +@end menu + +@node Linux/Unix +@section Linux/Unix + +@subsection Compilation + +First you must decompress the sources: +@example +cd /tmp +tar zxvf qemu-x.y.z.tar.gz +cd qemu-x.y.z +@end example + +Then you configure QEMU and build it (usually no options are needed): +@example +./configure +make +@end example + +Then type as root user: +@example +make install +@end example +to install QEMU in @file{/usr/local}. + +@subsection Tested tool versions + +In order to compile QEMU succesfully, it is very important that you +have the right tools. The most important one is gcc. I cannot guaranty +that QEMU works if you do not use a tested gcc version. Look at +'configure' and 'Makefile' if you want to make a different gcc +version work. + +@example +host gcc binutils glibc linux distribution +---------------------------------------------------------------------- +x86 3.2 2.13.2 2.1.3 2.4.18 + 2.96 2.11.93.0.2 2.2.5 2.4.18 Red Hat 7.3 + 3.2.2 2.13.90.0.18 2.3.2 2.4.20 Red Hat 9 + +PowerPC 3.3 [4] 2.13.90.0.18 2.3.1 2.4.20briq + 3.2 + +Alpha 3.3 [1] 2.14.90.0.4 2.2.5 2.2.20 [2] Debian 3.0 + +Sparc32 2.95.4 2.12.90.0.1 2.2.5 2.4.18 Debian 3.0 + +ARM 2.95.4 2.12.90.0.1 2.2.5 2.4.9 [3] Debian 3.0 + +[1] On Alpha, QEMU needs the gcc 'visibility' attribute only available + for gcc version >= 3.3. +[2] Linux >= 2.4.20 is necessary for precise exception support + (untested). +[3] 2.4.9-ac10-rmk2-np1-cerf2 + +[4] gcc 2.95.x generates invalid code when using too many register +variables. You must use gcc 3.x on PowerPC. +@end example + +@node Windows +@section Windows + +@itemize +@item Install the current versions of MSYS and MinGW from +@url{http://www.mingw.org/}. You can find detailed installation +instructions in the download section and the FAQ. + +@item Download +the MinGW development library of SDL 1.2.x +(@file{SDL-devel-1.2.x-@/mingw32.tar.gz}) from +@url{http://www.libsdl.org}. Unpack it in a temporary place, and +unpack the archive @file{i386-mingw32msvc.tar.gz} in the MinGW tool +directory. Edit the @file{sdl-config} script so that it gives the +correct SDL directory when invoked. + +@item Extract the current version of QEMU. + +@item Start the MSYS shell (file @file{msys.bat}). + +@item Change to the QEMU directory. Launch @file{./configure} and +@file{make}. If you have problems using SDL, verify that +@file{sdl-config} can be launched from the MSYS command line. + +@item You can install QEMU in @file{Program Files/Qemu} by typing +@file{make install}. Don't forget to copy @file{SDL.dll} in +@file{Program Files/Qemu}. + +@end itemize + +@node Cross compilation for Windows with Linux +@section Cross compilation for Windows with Linux + +@itemize +@item +Install the MinGW cross compilation tools available at +@url{http://www.mingw.org/}. + +@item +Install the Win32 version of SDL (@url{http://www.libsdl.org}) by +unpacking @file{i386-mingw32msvc.tar.gz}. Set up the PATH environment +variable so that @file{i386-mingw32msvc-sdl-config} can be launched by +the QEMU configuration script. + +@item +Configure QEMU for Windows cross compilation: +@example +./configure --enable-mingw32 +@end example +If necessary, you can change the cross-prefix according to the prefix +choosen for the MinGW tools with --cross-prefix. You can also use +--prefix to set the Win32 install path. + +@item You can install QEMU in the installation directory by typing +@file{make install}. Don't forget to copy @file{SDL.dll} in the +installation directory. + +@end itemize + +Note: Currently, Wine does not seem able to launch +QEMU for Win32. + +@node Mac OS X +@section Mac OS X + +The Mac OS X patches are not fully merged in QEMU, so you should look +at the QEMU mailing list archive to have all the necessary +information. + +@node Index +@chapter Index +@printindex cp + +@bye diff --git a/tools/ioemu/qemu-img.c b/tools/ioemu/qemu-img.c new file mode 100644 index 0000000000..3a18c93254 --- /dev/null +++ b/tools/ioemu/qemu-img.c @@ -0,0 +1,699 @@ +/* + * create a COW disk image + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +void *get_mmap_addr(unsigned long size) +{ + return NULL; +} + +void qemu_free(void *ptr) +{ + free(ptr); +} + +void *qemu_malloc(size_t size) +{ + return malloc(size); +} + +void *qemu_mallocz(size_t size) +{ + void *ptr; + ptr = qemu_malloc(size); + if (!ptr) + return NULL; + memset(ptr, 0, size); + return ptr; +} + +char *qemu_strdup(const char *str) +{ + char *ptr; + ptr = qemu_malloc(strlen(str) + 1); + if (!ptr) + return NULL; + strcpy(ptr, str); + return ptr; +} + +void pstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + if (buf_size <= 0) + return; + + for(;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = c; + } + *q = '\0'; +} + +/* strcat and truncate. */ +char *pstrcat(char *buf, int buf_size, const char *s) +{ + int len; + len = strlen(buf); + if (len < buf_size) + pstrcpy(buf + len, buf_size - len, s); + return buf; +} + +int strstart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (*p != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +void term_printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +void __attribute__((noreturn)) error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "qemu-img: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + exit(1); + va_end(ap); +} + +static void format_print(void *opaque, const char *name) +{ + printf(" %s", name); +} + +void help(void) +{ + printf("qemu-img version " QEMU_VERSION ", Copyright (c) 2004-2005 Fabrice Bellard\n" + "usage: qemu-img command [command options]\n" + "QEMU disk image utility\n" + "\n" + "Command syntax:\n" + " create [-e] [-b base_image] [-f fmt] filename [size]\n" + " commit [-f fmt] filename\n" + " convert [-c] [-e] [-f fmt] filename [-O output_fmt] output_filename\n" + " info [-f fmt] filename\n" + "\n" + "Command parameters:\n" + " 'filename' is a disk image filename\n" + " 'base_image' is the read-only disk image which is used as base for a copy on\n" + " write image; the copy on write image only stores the modified data\n" + " 'fmt' is the disk image format. It is guessed automatically in most cases\n" + " 'size' is the disk image size in kilobytes. Optional suffixes 'M' (megabyte)\n" + " and 'G' (gigabyte) are supported\n" + " 'output_filename' is the destination disk image filename\n" + " 'output_fmt' is the destination format\n" + " '-c' indicates that target image must be compressed (qcow format only)\n" + " '-e' indicates that the target image must be encrypted (qcow format only)\n" + ); + printf("\nSupported format:"); + bdrv_iterate_format(format_print, NULL); + printf("\n"); + exit(1); +} + + +#define NB_SUFFIXES 4 + +static void get_human_readable_size(char *buf, int buf_size, int64_t size) +{ + char suffixes[NB_SUFFIXES] = "KMGT"; + int64_t base; + int i; + + if (size <= 999) { + snprintf(buf, buf_size, "%lld", (long long) size); + } else { + base = 1024; + for(i = 0; i < NB_SUFFIXES; i++) { + if (size < (10 * base)) { + snprintf(buf, buf_size, "%0.1f%c", + (double)size / base, + suffixes[i]); + break; + } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) { + snprintf(buf, buf_size, "%lld%c", + (long long) ((size + (base >> 1)) / base), + suffixes[i]); + break; + } + base = base * 1024; + } + } +} + +#if defined(WIN32) +/* XXX: put correct support for win32 */ +static int read_password(char *buf, int buf_size) +{ + int c, i; + printf("Password: "); + fflush(stdout); + i = 0; + for(;;) { + c = getchar(); + if (c == '\n') + break; + if (i < (buf_size - 1)) + buf[i++] = c; + } + buf[i] = '\0'; + return 0; +} + +#else + +#include + +static struct termios oldtty; + +static void term_exit(void) +{ + tcsetattr (0, TCSANOW, &oldtty); +} + +static void term_init(void) +{ + struct termios tty; + + tcgetattr (0, &tty); + oldtty = tty; + + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + tty.c_oflag |= OPOST; + tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); + tty.c_cflag &= ~(CSIZE|PARENB); + tty.c_cflag |= CS8; + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + + tcsetattr (0, TCSANOW, &tty); + + atexit(term_exit); +} + +int read_password(char *buf, int buf_size) +{ + uint8_t ch; + int i, ret; + + printf("password: "); + fflush(stdout); + term_init(); + i = 0; + for(;;) { + ret = read(0, &ch, 1); + if (ret == -1) { + if (errno == EAGAIN || errno == EINTR) { + continue; + } else { + ret = -1; + break; + } + } else if (ret == 0) { + ret = -1; + break; + } else { + if (ch == '\r') { + ret = 0; + break; + } + if (i < (buf_size - 1)) + buf[i++] = ch; + } + } + term_exit(); + buf[i] = '\0'; + printf("\n"); + return ret; +} +#endif + +static BlockDriverState *bdrv_new_open(const char *filename, + const char *fmt) +{ + BlockDriverState *bs; + BlockDriver *drv; + char password[256]; + + bs = bdrv_new(""); + if (!bs) + error("Not enough memory"); + if (fmt) { + drv = bdrv_find_format(fmt); + if (!drv) + error("Unknown file format '%s'", fmt); + } else { + drv = NULL; + } + if (bdrv_open2(bs, filename, 0, drv) < 0) { + error("Could not open '%s'", filename); + } + if (bdrv_is_encrypted(bs)) { + printf("Disk image '%s' is encrypted.\n", filename); + if (read_password(password, sizeof(password)) < 0) + error("No password given"); + if (bdrv_set_key(bs, password) < 0) + error("invalid password"); + } + return bs; +} + +static int img_create(int argc, char **argv) +{ + int c, ret, encrypted; + const char *fmt = "raw"; + const char *filename; + const char *base_filename = NULL; + int64_t size; + const char *p; + BlockDriver *drv; + + encrypted = 0; + for(;;) { + c = getopt(argc, argv, "b:f:he"); + if (c == -1) + break; + switch(c) { + case 'h': + help(); + break; + case 'b': + base_filename = optarg; + break; + case 'f': + fmt = optarg; + break; + case 'e': + encrypted = 1; + break; + } + } + if (optind >= argc) + help(); + filename = argv[optind++]; + size = 0; + if (base_filename) { + BlockDriverState *bs; + bs = bdrv_new_open(base_filename, NULL); + bdrv_get_geometry(bs, &size); + size *= 512; + bdrv_delete(bs); + } else { + if (optind >= argc) + help(); + p = argv[optind]; + size = strtoul(p, (char **)&p, 0); + if (*p == 'M') { + size *= 1024 * 1024; + } else if (*p == 'G') { + size *= 1024 * 1024 * 1024; + } else if (*p == 'k' || *p == 'K' || *p == '\0') { + size *= 1024; + } else { + help(); + } + } + drv = bdrv_find_format(fmt); + if (!drv) + error("Unknown file format '%s'", fmt); + printf("Formating '%s', fmt=%s", + filename, fmt); + if (encrypted) + printf(", encrypted"); + if (base_filename) { + printf(", backing_file=%s", + base_filename); + } + printf(", size=%lld kB\n", (long long) (size / 1024)); + ret = bdrv_create(drv, filename, size / 512, base_filename, encrypted); + if (ret < 0) { + if (ret == -ENOTSUP) { + error("Formatting or formatting option not supported for file format '%s'", fmt); + } else { + error("Error while formatting"); + } + } + return 0; +} + +static int img_commit(int argc, char **argv) +{ + int c, ret; + const char *filename, *fmt; + BlockDriver *drv; + BlockDriverState *bs; + + fmt = NULL; + for(;;) { + c = getopt(argc, argv, "f:h"); + if (c == -1) + break; + switch(c) { + case 'h': + help(); + break; + case 'f': + fmt = optarg; + break; + } + } + if (optind >= argc) + help(); + filename = argv[optind++]; + + bs = bdrv_new(""); + if (!bs) + error("Not enough memory"); + if (fmt) { + drv = bdrv_find_format(fmt); + if (!drv) + error("Unknown file format '%s'", fmt); + } else { + drv = NULL; + } + if (bdrv_open2(bs, filename, 0, drv) < 0) { + error("Could not open '%s'", filename); + } + ret = bdrv_commit(bs); + switch(ret) { + case 0: + printf("Image committed.\n"); + break; + case -ENOENT: + error("No disk inserted"); + break; + case -EACCES: + error("Image is read-only"); + break; + case -ENOTSUP: + error("Image is already committed"); + break; + default: + error("Error while committing image"); + break; + } + + bdrv_delete(bs); + return 0; +} + +static int is_not_zero(const uint8_t *sector, int len) +{ + int i; + len >>= 2; + for(i = 0;i < len; i++) { + if (((uint32_t *)sector)[i] != 0) + return 1; + } + return 0; +} + +static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum) +{ + int v, i; + + if (n <= 0) { + *pnum = 0; + return 0; + } + v = is_not_zero(buf, 512); + for(i = 1; i < n; i++) { + buf += 512; + if (v != is_not_zero(buf, 512)) + break; + } + *pnum = i; + return v; +} + +#define IO_BUF_SIZE 65536 + +static int img_convert(int argc, char **argv) +{ + int c, ret, n, n1, compress, cluster_size, cluster_sectors, encrypt; + const char *filename, *fmt, *out_fmt, *out_filename; + BlockDriver *drv; + BlockDriverState *bs, *out_bs; + int64_t total_sectors, nb_sectors, sector_num; + uint8_t buf[IO_BUF_SIZE]; + const uint8_t *buf1; + + fmt = NULL; + out_fmt = "raw"; + compress = 0; + encrypt = 0; + for(;;) { + c = getopt(argc, argv, "f:O:hce"); + if (c == -1) + break; + switch(c) { + case 'h': + help(); + break; + case 'f': + fmt = optarg; + break; + case 'O': + out_fmt = optarg; + break; + case 'c': + compress = 1; + break; + case 'e': + encrypt = 1; + break; + } + } + if (optind >= argc) + help(); + filename = argv[optind++]; + if (optind >= argc) + help(); + out_filename = argv[optind++]; + + bs = bdrv_new_open(filename, fmt); + + drv = bdrv_find_format(out_fmt); + if (!drv) + error("Unknown file format '%s'", fmt); + if (compress && drv != &bdrv_qcow) + error("Compression not supported for this file format"); + if (encrypt && drv != &bdrv_qcow) + error("Encryption not supported for this file format"); + if (compress && encrypt) + error("Compression and encryption not supported at the same time"); + bdrv_get_geometry(bs, &total_sectors); + ret = bdrv_create(drv, out_filename, total_sectors, NULL, encrypt); + if (ret < 0) { + if (ret == -ENOTSUP) { + error("Formatting not supported for file format '%s'", fmt); + } else { + error("Error while formatting '%s'", out_filename); + } + } + + out_bs = bdrv_new_open(out_filename, out_fmt); + + if (compress) { + cluster_size = qcow_get_cluster_size(out_bs); + if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE) + error("invalid cluster size"); + cluster_sectors = cluster_size >> 9; + sector_num = 0; + for(;;) { + nb_sectors = total_sectors - sector_num; + if (nb_sectors <= 0) + break; + if (nb_sectors >= cluster_sectors) + n = cluster_sectors; + else + n = nb_sectors; + if (bdrv_read(bs, sector_num, buf, n) < 0) + error("error while reading"); + if (n < cluster_sectors) + memset(buf + n * 512, 0, cluster_size - n * 512); + if (is_not_zero(buf, cluster_size)) { + if (qcow_compress_cluster(out_bs, sector_num, buf) != 0) + error("error while compressing sector %lld", sector_num); + } + sector_num += n; + } + } else { + sector_num = 0; + for(;;) { + nb_sectors = total_sectors - sector_num; + if (nb_sectors <= 0) + break; + if (nb_sectors >= (IO_BUF_SIZE / 512)) + n = (IO_BUF_SIZE / 512); + else + n = nb_sectors; + if (bdrv_read(bs, sector_num, buf, n) < 0) + error("error while reading"); + /* NOTE: at the same time we convert, we do not write zero + sectors to have a chance to compress the image. Ideally, we + should add a specific call to have the info to go faster */ + buf1 = buf; + while (n > 0) { + if (is_allocated_sectors(buf1, n, &n1)) { + if (bdrv_write(out_bs, sector_num, buf1, n1) < 0) + error("error while writing"); + } + sector_num += n1; + n -= n1; + buf1 += n1 * 512; + } + } + } + bdrv_delete(out_bs); + bdrv_delete(bs); + return 0; +} + +#ifdef _WIN32 +static int64_t get_allocated_file_size(const char *filename) +{ + struct _stati64 st; + if (_stati64(filename, &st) < 0) + return -1; + return st.st_size; +} +#else +static int64_t get_allocated_file_size(const char *filename) +{ + struct stat st; + if (stat(filename, &st) < 0) + return -1; + return (int64_t)st.st_blocks * 512; +} +#endif + +static int img_info(int argc, char **argv) +{ + int c; + const char *filename, *fmt; + BlockDriver *drv; + BlockDriverState *bs; + char fmt_name[128], size_buf[128], dsize_buf[128]; + int64_t total_sectors, allocated_size; + + fmt = NULL; + for(;;) { + c = getopt(argc, argv, "f:h"); + if (c == -1) + break; + switch(c) { + case 'h': + help(); + break; + case 'f': + fmt = optarg; + break; + } + } + if (optind >= argc) + help(); + filename = argv[optind++]; + + bs = bdrv_new(""); + if (!bs) + error("Not enough memory"); + if (fmt) { + drv = bdrv_find_format(fmt); + if (!drv) + error("Unknown file format '%s'", fmt); + } else { + drv = NULL; + } + if (bdrv_open2(bs, filename, 0, drv) < 0) { + error("Could not open '%s'", filename); + } + bdrv_get_format(bs, fmt_name, sizeof(fmt_name)); + bdrv_get_geometry(bs, &total_sectors); + get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512); + allocated_size = get_allocated_file_size(filename); + if (allocated_size < 0) + sprintf(dsize_buf, "unavailable"); + else + get_human_readable_size(dsize_buf, sizeof(dsize_buf), + allocated_size); + printf("image: %s\n" + "file format: %s\n" + "virtual size: %s (%lld bytes)\n" + "disk size: %s\n", + filename, fmt_name, size_buf, + (long long) (total_sectors * 512), + dsize_buf); + if (bdrv_is_encrypted(bs)) + printf("encrypted: yes\n"); + bdrv_delete(bs); + return 0; +} + +int main(int argc, char **argv) +{ + const char *cmd; + + bdrv_init(); + if (argc < 2) + help(); + cmd = argv[1]; + optind++; + if (!strcmp(cmd, "create")) { + img_create(argc, argv); + } else if (!strcmp(cmd, "commit")) { + img_commit(argc, argv); + } else if (!strcmp(cmd, "convert")) { + img_convert(argc, argv); + } else if (!strcmp(cmd, "info")) { + img_info(argc, argv); + } else { + help(); + } + return 0; +} diff --git a/tools/ioemu/qemu-img.texi b/tools/ioemu/qemu-img.texi new file mode 100644 index 0000000000..ac7923ffd1 --- /dev/null +++ b/tools/ioemu/qemu-img.texi @@ -0,0 +1,126 @@ +@example +@c man begin SYNOPSIS +usage: qemu-img command [command options] +@c man end +@end example + +@c man begin OPTIONS + +The following commands are supported: +@table @option +@item create [-e] [-b @var{base_image}] [-f @var{fmt}] @var{filename} [@var{size}] +@item commit [-f @var{fmt}] @var{filename} +@item convert [-c] [-e] [-f @var{fmt}] @var{filename} [-O @var{output_fmt}] @var{output_filename} +@item info [-f @var{fmt}] @var{filename} +@end table + +Command parameters: +@table @var +@item filename + is a disk image filename +@item base_image +is the read-only disk image which is used as base for a copy on + write image; the copy on write image only stores the modified data + +@item fmt +is the disk image format. It is guessed automatically in most cases. The following formats are supported: + +@table @code +@item raw + +Raw disk image format (default). This format has the advantage of +being simple and easily exportable to all other emulators. If your file +system supports @emph{holes} (for example in ext2 or ext3 on Linux), +then only the written sectors will reserve space. Use @code{qemu-img +info} to know the real size used by the image or @code{ls -ls} on +Unix/Linux. + +@item qcow +QEMU image format, the most versatile format. Use it to have smaller +images (useful if your filesystem does not supports holes, for example +on Windows), optional AES encryption and zlib based compression. +@item cow +User Mode Linux Copy On Write image format. Used to be the only growable +image format in QEMU. It is supported only for compatibility with +previous versions. It does not work on win32. +@item vmdk +VMware 3 and 4 compatible image format. +@item cloop +Linux Compressed Loop image, useful only to reuse directly compressed +CD-ROM images present for example in the Knoppix CD-ROMs. +@end table + +@item size +is the disk image size in kilobytes. Optional suffixes @code{M} +(megabyte) and @code{G} (gigabyte) are supported + +@item output_filename +is the destination disk image filename + +@item output_fmt + is the destination format + +@item -c +indicates that target image must be compressed (qcow format only) +@item -e +indicates that the target image must be encrypted (qcow format only) +@end table + +Command description: + +@table @option +@item create [-e] [-b @var{base_image}] [-f @var{fmt}] @var{filename} [@var{size}] + +Create the new disk image @var{filename} of size @var{size} and format +@var{fmt}. + +If @var{base_image} is specified, then the image will record only the +differences from @var{base_image}. No size needs to be specified in +this case. @var{base_image} will never be modified unless you use the +@code{commit} monitor command. + +@item commit [-f @var{fmt}] @var{filename} + +Commit the changes recorded in @var{filename} in its base image. + +@item convert [-c] [-e] [-f @var{fmt}] @var{filename} [-O @var{output_fmt}] @var{output_filename} + +Convert the disk image @var{filename} to disk image @var{output_filename} +using format @var{output_fmt}. It can be optionnaly encrypted +(@code{-e} option) or compressed (@code{-c} option). + +Only the format @code{qcow} supports encryption or compression. The +compression is read-only. It means that if a compressed sector is +rewritten, then it is rewritten as uncompressed data. + +Encryption uses the AES format which is very secure (128 bit keys). Use +a long password (16 characters) to get maximum protection. + +Image conversion is also useful to get smaller image when using a +growable format such as @code{qcow} or @code{cow}: the empty sectors +are detected and suppressed from the destination image. + +@item info [-f @var{fmt}] @var{filename} + +Give information about the disk image @var{filename}. Use it in +particular to know the size reserved on disk which can be different +from the displayed size. +@end table + +@c man end + +@ignore + +@setfilename qemu-img +@settitle QEMU disk image utility + +@c man begin SEEALSO +The HTML documentation of QEMU for more precise information and Linux +user mode emulator invocation. +@c man end + +@c man begin AUTHOR +Fabrice Bellard +@c man end + +@end ignore diff --git a/tools/ioemu/qemu-tech.texi b/tools/ioemu/qemu-tech.texi new file mode 100644 index 0000000000..77bda8637e --- /dev/null +++ b/tools/ioemu/qemu-tech.texi @@ -0,0 +1,595 @@ +\input texinfo @c -*- texinfo -*- +@c %**start of header +@setfilename qemu-tech.info +@settitle QEMU Internals +@exampleindent 0 +@paragraphindent 0 +@c %**end of header + +@iftex +@titlepage +@sp 7 +@center @titlefont{QEMU Internals} +@sp 3 +@end titlepage +@end iftex + +@ifnottex +@node Top +@top + +@menu +* Introduction:: +* QEMU Internals:: +* Regression Tests:: +* Index:: +@end menu +@end ifnottex + +@contents + +@node Introduction +@chapter Introduction + +@menu +* intro_features:: Features +* intro_x86_emulation:: x86 emulation +* intro_arm_emulation:: ARM emulation +* intro_ppc_emulation:: PowerPC emulation +* intro_sparc_emulation:: SPARC emulation +@end menu + +@node intro_features +@section Features + +QEMU is a FAST! processor emulator using a portable dynamic +translator. + +QEMU has two operating modes: + +@itemize @minus + +@item +Full system emulation. In this mode, QEMU emulates a full system +(usually a PC), including a processor and various peripherals. It can +be used to launch an different Operating System without rebooting the +PC or to debug system code. + +@item +User mode emulation (Linux host only). In this mode, QEMU can launch +Linux processes compiled for one CPU on another CPU. It can be used to +launch the Wine Windows API emulator (@url{http://www.winehq.org}) or +to ease cross-compilation and cross-debugging. + +@end itemize + +As QEMU requires no host kernel driver to run, it is very safe and +easy to use. + +QEMU generic features: + +@itemize + +@item User space only or full system emulation. + +@item Using dynamic translation to native code for reasonable speed. + +@item Working on x86 and PowerPC hosts. Being tested on ARM, Sparc32, Alpha and S390. + +@item Self-modifying code support. + +@item Precise exceptions support. + +@item The virtual CPU is a library (@code{libqemu}) which can be used +in other projects (look at @file{qemu/tests/qruncom.c} to have an +example of user mode @code{libqemu} usage). + +@end itemize + +QEMU user mode emulation features: +@itemize +@item Generic Linux system call converter, including most ioctls. + +@item clone() emulation using native CPU clone() to use Linux scheduler for threads. + +@item Accurate signal handling by remapping host signals to target signals. +@end itemize + +QEMU full system emulation features: +@itemize +@item QEMU can either use a full software MMU for maximum portability or use the host system call mmap() to simulate the target MMU. +@end itemize + +@node intro_x86_emulation +@section x86 emulation + +QEMU x86 target features: + +@itemize + +@item The virtual x86 CPU supports 16 bit and 32 bit addressing with segmentation. +LDT/GDT and IDT are emulated. VM86 mode is also supported to run DOSEMU. + +@item Support of host page sizes bigger than 4KB in user mode emulation. + +@item QEMU can emulate itself on x86. + +@item An extensive Linux x86 CPU test program is included @file{tests/test-i386}. +It can be used to test other x86 virtual CPUs. + +@end itemize + +Current QEMU limitations: + +@itemize + +@item No SSE/MMX support (yet). + +@item No x86-64 support. + +@item IPC syscalls are missing. + +@item The x86 segment limits and access rights are not tested at every +memory access (yet). Hopefully, very few OSes seem to rely on that for +normal use. + +@item On non x86 host CPUs, @code{double}s are used instead of the non standard +10 byte @code{long double}s of x86 for floating point emulation to get +maximum performances. + +@end itemize + +@node intro_arm_emulation +@section ARM emulation + +@itemize + +@item Full ARM 7 user emulation. + +@item NWFPE FPU support included in user Linux emulation. + +@item Can run most ARM Linux binaries. + +@end itemize + +@node intro_ppc_emulation +@section PowerPC emulation + +@itemize + +@item Full PowerPC 32 bit emulation, including privileged instructions, +FPU and MMU. + +@item Can run most PowerPC Linux binaries. + +@end itemize + +@node intro_sparc_emulation +@section SPARC emulation + +@itemize + +@item Somewhat complete SPARC V8 emulation, including privileged +instructions, FPU and MMU. SPARC V9 emulation includes most privileged +instructions, FPU and I/D MMU, but misses VIS instructions. + +@item Can run some 32-bit SPARC Linux binaries. + +@end itemize + +Current QEMU limitations: + +@itemize + +@item Tagged add/subtract instructions are not supported, but they are +probably not used. + +@item IPC syscalls are missing. + +@item 128-bit floating point operations are not supported, though none of the +real CPUs implement them either. FCMPE[SD] are not correctly +implemented. Floating point exception support is untested. + +@item Alignment is not enforced at all. + +@item Atomic instructions are not correctly implemented. + +@item Sparc64 emulators are not usable for anything yet. + +@end itemize + +@node QEMU Internals +@chapter QEMU Internals + +@menu +* QEMU compared to other emulators:: +* Portable dynamic translation:: +* Register allocation:: +* Condition code optimisations:: +* CPU state optimisations:: +* Translation cache:: +* Direct block chaining:: +* Self-modifying code and translated code invalidation:: +* Exception support:: +* MMU emulation:: +* Hardware interrupts:: +* User emulation specific details:: +* Bibliography:: +@end menu + +@node QEMU compared to other emulators +@section QEMU compared to other emulators + +Like bochs [3], QEMU emulates an x86 CPU. But QEMU is much faster than +bochs as it uses dynamic compilation. Bochs is closely tied to x86 PC +emulation while QEMU can emulate several processors. + +Like Valgrind [2], QEMU does user space emulation and dynamic +translation. Valgrind is mainly a memory debugger while QEMU has no +support for it (QEMU could be used to detect out of bound memory +accesses as Valgrind, but it has no support to track uninitialised data +as Valgrind does). The Valgrind dynamic translator generates better code +than QEMU (in particular it does register allocation) but it is closely +tied to an x86 host and target and has no support for precise exceptions +and system emulation. + +EM86 [4] is the closest project to user space QEMU (and QEMU still uses +some of its code, in particular the ELF file loader). EM86 was limited +to an alpha host and used a proprietary and slow interpreter (the +interpreter part of the FX!32 Digital Win32 code translator [5]). + +TWIN [6] is a Windows API emulator like Wine. It is less accurate than +Wine but includes a protected mode x86 interpreter to launch x86 Windows +executables. Such an approach has greater potential because most of the +Windows API is executed natively but it is far more difficult to develop +because all the data structures and function parameters exchanged +between the API and the x86 code must be converted. + +User mode Linux [7] was the only solution before QEMU to launch a +Linux kernel as a process while not needing any host kernel +patches. However, user mode Linux requires heavy kernel patches while +QEMU accepts unpatched Linux kernels. The price to pay is that QEMU is +slower. + +The new Plex86 [8] PC virtualizer is done in the same spirit as the +qemu-fast system emulator. It requires a patched Linux kernel to work +(you cannot launch the same kernel on your PC), but the patches are +really small. As it is a PC virtualizer (no emulation is done except +for some priveledged instructions), it has the potential of being +faster than QEMU. The downside is that a complicated (and potentially +unsafe) host kernel patch is needed. + +The commercial PC Virtualizers (VMWare [9], VirtualPC [10], TwoOStwo +[11]) are faster than QEMU, but they all need specific, proprietary +and potentially unsafe host drivers. Moreover, they are unable to +provide cycle exact simulation as an emulator can. + +@node Portable dynamic translation +@section Portable dynamic translation + +QEMU is a dynamic translator. When it first encounters a piece of code, +it converts it to the host instruction set. Usually dynamic translators +are very complicated and highly CPU dependent. QEMU uses some tricks +which make it relatively easily portable and simple while achieving good +performances. + +The basic idea is to split every x86 instruction into fewer simpler +instructions. Each simple instruction is implemented by a piece of C +code (see @file{target-i386/op.c}). Then a compile time tool +(@file{dyngen}) takes the corresponding object file (@file{op.o}) +to generate a dynamic code generator which concatenates the simple +instructions to build a function (see @file{op.h:dyngen_code()}). + +In essence, the process is similar to [1], but more work is done at +compile time. + +A key idea to get optimal performances is that constant parameters can +be passed to the simple operations. For that purpose, dummy ELF +relocations are generated with gcc for each constant parameter. Then, +the tool (@file{dyngen}) can locate the relocations and generate the +appriopriate C code to resolve them when building the dynamic code. + +That way, QEMU is no more difficult to port than a dynamic linker. + +To go even faster, GCC static register variables are used to keep the +state of the virtual CPU. + +@node Register allocation +@section Register allocation + +Since QEMU uses fixed simple instructions, no efficient register +allocation can be done. However, because RISC CPUs have a lot of +register, most of the virtual CPU state can be put in registers without +doing complicated register allocation. + +@node Condition code optimisations +@section Condition code optimisations + +Good CPU condition codes emulation (@code{EFLAGS} register on x86) is a +critical point to get good performances. QEMU uses lazy condition code +evaluation: instead of computing the condition codes after each x86 +instruction, it just stores one operand (called @code{CC_SRC}), the +result (called @code{CC_DST}) and the type of operation (called +@code{CC_OP}). + +@code{CC_OP} is almost never explicitely set in the generated code +because it is known at translation time. + +In order to increase performances, a backward pass is performed on the +generated simple instructions (see +@code{target-i386/translate.c:optimize_flags()}). When it can be proved that +the condition codes are not needed by the next instructions, no +condition codes are computed at all. + +@node CPU state optimisations +@section CPU state optimisations + +The x86 CPU has many internal states which change the way it evaluates +instructions. In order to achieve a good speed, the translation phase +considers that some state information of the virtual x86 CPU cannot +change in it. For example, if the SS, DS and ES segments have a zero +base, then the translator does not even generate an addition for the +segment base. + +[The FPU stack pointer register is not handled that way yet]. + +@node Translation cache +@section Translation cache + +A 16 MByte cache holds the most recently used translations. For +simplicity, it is completely flushed when it is full. A translation unit +contains just a single basic block (a block of x86 instructions +terminated by a jump or by a virtual CPU state change which the +translator cannot deduce statically). + +@node Direct block chaining +@section Direct block chaining + +After each translated basic block is executed, QEMU uses the simulated +Program Counter (PC) and other cpu state informations (such as the CS +segment base value) to find the next basic block. + +In order to accelerate the most common cases where the new simulated PC +is known, QEMU can patch a basic block so that it jumps directly to the +next one. + +The most portable code uses an indirect jump. An indirect jump makes +it easier to make the jump target modification atomic. On some host +architectures (such as x86 or PowerPC), the @code{JUMP} opcode is +directly patched so that the block chaining has no overhead. + +@node Self-modifying code and translated code invalidation +@section Self-modifying code and translated code invalidation + +Self-modifying code is a special challenge in x86 emulation because no +instruction cache invalidation is signaled by the application when code +is modified. + +When translated code is generated for a basic block, the corresponding +host page is write protected if it is not already read-only (with the +system call @code{mprotect()}). Then, if a write access is done to the +page, Linux raises a SEGV signal. QEMU then invalidates all the +translated code in the page and enables write accesses to the page. + +Correct translated code invalidation is done efficiently by maintaining +a linked list of every translated block contained in a given page. Other +linked lists are also maintained to undo direct block chaining. + +Although the overhead of doing @code{mprotect()} calls is important, +most MSDOS programs can be emulated at reasonnable speed with QEMU and +DOSEMU. + +Note that QEMU also invalidates pages of translated code when it detects +that memory mappings are modified with @code{mmap()} or @code{munmap()}. + +When using a software MMU, the code invalidation is more efficient: if +a given code page is invalidated too often because of write accesses, +then a bitmap representing all the code inside the page is +built. Every store into that page checks the bitmap to see if the code +really needs to be invalidated. It avoids invalidating the code when +only data is modified in the page. + +@node Exception support +@section Exception support + +longjmp() is used when an exception such as division by zero is +encountered. + +The host SIGSEGV and SIGBUS signal handlers are used to get invalid +memory accesses. The exact CPU state can be retrieved because all the +x86 registers are stored in fixed host registers. The simulated program +counter is found by retranslating the corresponding basic block and by +looking where the host program counter was at the exception point. + +The virtual CPU cannot retrieve the exact @code{EFLAGS} register because +in some cases it is not computed because of condition code +optimisations. It is not a big concern because the emulated code can +still be restarted in any cases. + +@node MMU emulation +@section MMU emulation + +For system emulation, QEMU uses the mmap() system call to emulate the +target CPU MMU. It works as long the emulated OS does not use an area +reserved by the host OS (such as the area above 0xc0000000 on x86 +Linux). + +In order to be able to launch any OS, QEMU also supports a soft +MMU. In that mode, the MMU virtual to physical address translation is +done at every memory access. QEMU uses an address translation cache to +speed up the translation. + +In order to avoid flushing the translated code each time the MMU +mappings change, QEMU uses a physically indexed translation cache. It +means that each basic block is indexed with its physical address. + +When MMU mappings change, only the chaining of the basic blocks is +reset (i.e. a basic block can no longer jump directly to another one). + +@node Hardware interrupts +@section Hardware interrupts + +In order to be faster, QEMU does not check at every basic block if an +hardware interrupt is pending. Instead, the user must asynchrously +call a specific function to tell that an interrupt is pending. This +function resets the chaining of the currently executing basic +block. It ensures that the execution will return soon in the main loop +of the CPU emulator. Then the main loop can test if the interrupt is +pending and handle it. + +@node User emulation specific details +@section User emulation specific details + +@subsection Linux system call translation + +QEMU includes a generic system call translator for Linux. It means that +the parameters of the system calls can be converted to fix the +endianness and 32/64 bit issues. The IOCTLs are converted with a generic +type description system (see @file{ioctls.h} and @file{thunk.c}). + +QEMU supports host CPUs which have pages bigger than 4KB. It records all +the mappings the process does and try to emulated the @code{mmap()} +system calls in cases where the host @code{mmap()} call would fail +because of bad page alignment. + +@subsection Linux signals + +Normal and real-time signals are queued along with their information +(@code{siginfo_t}) as it is done in the Linux kernel. Then an interrupt +request is done to the virtual CPU. When it is interrupted, one queued +signal is handled by generating a stack frame in the virtual CPU as the +Linux kernel does. The @code{sigreturn()} system call is emulated to return +from the virtual signal handler. + +Some signals (such as SIGALRM) directly come from the host. Other +signals are synthetized from the virtual CPU exceptions such as SIGFPE +when a division by zero is done (see @code{main.c:cpu_loop()}). + +The blocked signal mask is still handled by the host Linux kernel so +that most signal system calls can be redirected directly to the host +Linux kernel. Only the @code{sigaction()} and @code{sigreturn()} system +calls need to be fully emulated (see @file{signal.c}). + +@subsection clone() system call and threads + +The Linux clone() system call is usually used to create a thread. QEMU +uses the host clone() system call so that real host threads are created +for each emulated thread. One virtual CPU instance is created for each +thread. + +The virtual x86 CPU atomic operations are emulated with a global lock so +that their semantic is preserved. + +Note that currently there are still some locking issues in QEMU. In +particular, the translated cache flush is not protected yet against +reentrancy. + +@subsection Self-virtualization + +QEMU was conceived so that ultimately it can emulate itself. Although +it is not very useful, it is an important test to show the power of the +emulator. + +Achieving self-virtualization is not easy because there may be address +space conflicts. QEMU solves this problem by being an executable ELF +shared object as the ld-linux.so ELF interpreter. That way, it can be +relocated at load time. + +@node Bibliography +@section Bibliography + +@table @asis + +@item [1] +@url{http://citeseer.nj.nec.com/piumarta98optimizing.html}, Optimizing +direct threaded code by selective inlining (1998) by Ian Piumarta, Fabio +Riccardi. + +@item [2] +@url{http://developer.kde.org/~sewardj/}, Valgrind, an open-source +memory debugger for x86-GNU/Linux, by Julian Seward. + +@item [3] +@url{http://bochs.sourceforge.net/}, the Bochs IA-32 Emulator Project, +by Kevin Lawton et al. + +@item [4] +@url{http://www.cs.rose-hulman.edu/~donaldlf/em86/index.html}, the EM86 +x86 emulator on Alpha-Linux. + +@item [5] +@url{http://www.usenix.org/publications/library/proceedings/usenix-nt97/@/full_papers/chernoff/chernoff.pdf}, +DIGITAL FX!32: Running 32-Bit x86 Applications on Alpha NT, by Anton +Chernoff and Ray Hookway. + +@item [6] +@url{http://www.willows.com/}, Windows API library emulation from +Willows Software. + +@item [7] +@url{http://user-mode-linux.sourceforge.net/}, +The User-mode Linux Kernel. + +@item [8] +@url{http://www.plex86.org/}, +The new Plex86 project. + +@item [9] +@url{http://www.vmware.com/}, +The VMWare PC virtualizer. + +@item [10] +@url{http://www.microsoft.com/windowsxp/virtualpc/}, +The VirtualPC PC virtualizer. + +@item [11] +@url{http://www.twoostwo.org/}, +The TwoOStwo PC virtualizer. + +@end table + +@node Regression Tests +@chapter Regression Tests + +In the directory @file{tests/}, various interesting testing programs +are available. There are used for regression testing. + +@menu +* test-i386:: +* linux-test:: +* qruncom.c:: +@end menu + +@node test-i386 +@section @file{test-i386} + +This program executes most of the 16 bit and 32 bit x86 instructions and +generates a text output. It can be compared with the output obtained with +a real CPU or another emulator. The target @code{make test} runs this +program and a @code{diff} on the generated output. + +The Linux system call @code{modify_ldt()} is used to create x86 selectors +to test some 16 bit addressing and 32 bit with segmentation cases. + +The Linux system call @code{vm86()} is used to test vm86 emulation. + +Various exceptions are raised to test most of the x86 user space +exception reporting. + +@node linux-test +@section @file{linux-test} + +This program tests various Linux system calls. It is used to verify +that the system call parameters are correctly converted between target +and host CPUs. + +@node qruncom.c +@section @file{qruncom.c} + +Example of usage of @code{libqemu} to emulate a user mode i386 CPU. + +@node Index +@chapter Index +@printindex cp + +@bye diff --git a/tools/ioemu/qemu_socket.h b/tools/ioemu/qemu_socket.h new file mode 100644 index 0000000000..64b7d4e5ef --- /dev/null +++ b/tools/ioemu/qemu_socket.h @@ -0,0 +1,30 @@ +/* headers to use the BSD sockets */ +#ifndef QEMU_SOCKET_H +#define QEMU_SOCKET_H + +#ifdef _WIN32 + +#include +#include +#include + +#define socket_error() WSAGetLastError() +#undef EINTR +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EINTR WSAEINTR +#define EINPROGRESS WSAEINPROGRESS + +#else + +#include +#include +#include + +#define socket_error() errno +#define closesocket(s) close(s) + +#endif /* !_WIN32 */ + +void socket_set_nonblock(int fd); + +#endif /* QEMU_SOCKET_H */ diff --git a/tools/ioemu/readline.c b/tools/ioemu/readline.c new file mode 100644 index 0000000000..cbe33dbdd8 --- /dev/null +++ b/tools/ioemu/readline.c @@ -0,0 +1,425 @@ +/* + * QEMU readline utility + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#define TERM_CMD_BUF_SIZE 4095 +#define TERM_MAX_CMDS 64 +#define NB_COMPLETIONS_MAX 256 + +#define IS_NORM 0 +#define IS_ESC 1 +#define IS_CSI 2 + +#define printf do_not_use_printf + +static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1]; +static int term_cmd_buf_index; +static int term_cmd_buf_size; + +static char term_last_cmd_buf[TERM_CMD_BUF_SIZE + 1]; +static int term_last_cmd_buf_index; +static int term_last_cmd_buf_size; + +static int term_esc_state; +static int term_esc_param; + +static char *term_history[TERM_MAX_CMDS]; +static int term_hist_entry = -1; + +static int nb_completions; +int completion_index; +static char *completions[NB_COMPLETIONS_MAX]; + +static ReadLineFunc *term_readline_func; +static int term_is_password; +static char term_prompt[256]; +static void *term_readline_opaque; + +static void term_show_prompt2(void) +{ + term_printf("%s", term_prompt); + term_flush(); + term_last_cmd_buf_index = 0; + term_last_cmd_buf_size = 0; + term_esc_state = IS_NORM; +} + +static void term_show_prompt(void) +{ + term_show_prompt2(); + term_cmd_buf_index = 0; + term_cmd_buf_size = 0; +} + +/* update the displayed command line */ +static void term_update(void) +{ + int i, delta, len; + + if (term_cmd_buf_size != term_last_cmd_buf_size || + memcmp(term_cmd_buf, term_last_cmd_buf, term_cmd_buf_size) != 0) { + for(i = 0; i < term_last_cmd_buf_index; i++) { + term_printf("\033[D"); + } + term_cmd_buf[term_cmd_buf_size] = '\0'; + if (term_is_password) { + len = strlen(term_cmd_buf); + for(i = 0; i < len; i++) + term_printf("*"); + } else { + term_printf("%s", term_cmd_buf); + } + term_printf("\033[K"); + memcpy(term_last_cmd_buf, term_cmd_buf, term_cmd_buf_size); + term_last_cmd_buf_size = term_cmd_buf_size; + term_last_cmd_buf_index = term_cmd_buf_size; + } + if (term_cmd_buf_index != term_last_cmd_buf_index) { + delta = term_cmd_buf_index - term_last_cmd_buf_index; + if (delta > 0) { + for(i = 0;i < delta; i++) { + term_printf("\033[C"); + } + } else { + delta = -delta; + for(i = 0;i < delta; i++) { + term_printf("\033[D"); + } + } + term_last_cmd_buf_index = term_cmd_buf_index; + } + term_flush(); +} + +static void term_insert_char(int ch) +{ + if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) { + memmove(term_cmd_buf + term_cmd_buf_index + 1, + term_cmd_buf + term_cmd_buf_index, + term_cmd_buf_size - term_cmd_buf_index); + term_cmd_buf[term_cmd_buf_index] = ch; + term_cmd_buf_size++; + term_cmd_buf_index++; + } +} + +static void term_backward_char(void) +{ + if (term_cmd_buf_index > 0) { + term_cmd_buf_index--; + } +} + +static void term_forward_char(void) +{ + if (term_cmd_buf_index < term_cmd_buf_size) { + term_cmd_buf_index++; + } +} + +static void term_delete_char(void) +{ + if (term_cmd_buf_index < term_cmd_buf_size) { + memmove(term_cmd_buf + term_cmd_buf_index, + term_cmd_buf + term_cmd_buf_index + 1, + term_cmd_buf_size - term_cmd_buf_index - 1); + term_cmd_buf_size--; + } +} + +static void term_backspace(void) +{ + if (term_cmd_buf_index > 0) { + term_backward_char(); + term_delete_char(); + } +} + +static void term_bol(void) +{ + term_cmd_buf_index = 0; +} + +static void term_eol(void) +{ + term_cmd_buf_index = term_cmd_buf_size; +} + +static void term_up_char(void) +{ + int idx; + + if (term_hist_entry == 0) + return; + if (term_hist_entry == -1) { + /* Find latest entry */ + for (idx = 0; idx < TERM_MAX_CMDS; idx++) { + if (term_history[idx] == NULL) + break; + } + term_hist_entry = idx; + } + term_hist_entry--; + if (term_hist_entry >= 0) { + pstrcpy(term_cmd_buf, sizeof(term_cmd_buf), + term_history[term_hist_entry]); + term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf); + } +} + +static void term_down_char(void) +{ + if (term_hist_entry == TERM_MAX_CMDS - 1 || term_hist_entry == -1) + return; + if (term_history[++term_hist_entry] != NULL) { + pstrcpy(term_cmd_buf, sizeof(term_cmd_buf), + term_history[term_hist_entry]); + } else { + term_hist_entry = -1; + } + term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf); +} + +static void term_hist_add(const char *cmdline) +{ + char *hist_entry, *new_entry; + int idx; + + if (cmdline[0] == '\0') + return; + new_entry = NULL; + if (term_hist_entry != -1) { + /* We were editing an existing history entry: replace it */ + hist_entry = term_history[term_hist_entry]; + idx = term_hist_entry; + if (strcmp(hist_entry, cmdline) == 0) { + goto same_entry; + } + } + /* Search cmdline in history buffers */ + for (idx = 0; idx < TERM_MAX_CMDS; idx++) { + hist_entry = term_history[idx]; + if (hist_entry == NULL) + break; + if (strcmp(hist_entry, cmdline) == 0) { + same_entry: + new_entry = hist_entry; + /* Put this entry at the end of history */ + memmove(&term_history[idx], &term_history[idx + 1], + &term_history[TERM_MAX_CMDS] - &term_history[idx + 1]); + term_history[TERM_MAX_CMDS - 1] = NULL; + for (; idx < TERM_MAX_CMDS; idx++) { + if (term_history[idx] == NULL) + break; + } + break; + } + } + if (idx == TERM_MAX_CMDS) { + /* Need to get one free slot */ + free(term_history[0]); + memcpy(term_history, &term_history[1], + &term_history[TERM_MAX_CMDS] - &term_history[1]); + term_history[TERM_MAX_CMDS - 1] = NULL; + idx = TERM_MAX_CMDS - 1; + } + if (new_entry == NULL) + new_entry = strdup(cmdline); + term_history[idx] = new_entry; + term_hist_entry = -1; +} + +/* completion support */ + +void add_completion(const char *str) +{ + if (nb_completions < NB_COMPLETIONS_MAX) { + completions[nb_completions++] = qemu_strdup(str); + } +} + +static void term_completion(void) +{ + int len, i, j, max_width, nb_cols; + char *cmdline; + + nb_completions = 0; + + cmdline = qemu_malloc(term_cmd_buf_index + 1); + if (!cmdline) + return; + memcpy(cmdline, term_cmd_buf, term_cmd_buf_index); + cmdline[term_cmd_buf_index] = '\0'; + readline_find_completion(cmdline); + qemu_free(cmdline); + + /* no completion found */ + if (nb_completions <= 0) + return; + if (nb_completions == 1) { + len = strlen(completions[0]); + for(i = completion_index; i < len; i++) { + term_insert_char(completions[0][i]); + } + /* extra space for next argument. XXX: make it more generic */ + if (len > 0 && completions[0][len - 1] != '/') + term_insert_char(' '); + } else { + term_printf("\n"); + max_width = 0; + for(i = 0; i < nb_completions; i++) { + len = strlen(completions[i]); + if (len > max_width) + max_width = len; + } + max_width += 2; + if (max_width < 10) + max_width = 10; + else if (max_width > 80) + max_width = 80; + nb_cols = 80 / max_width; + j = 0; + for(i = 0; i < nb_completions; i++) { + term_printf("%-*s", max_width, completions[i]); + if (++j == nb_cols || i == (nb_completions - 1)) { + term_printf("\n"); + j = 0; + } + } + term_show_prompt2(); + } +} + +/* return true if command handled */ +void readline_handle_byte(int ch) +{ + switch(term_esc_state) { + case IS_NORM: + switch(ch) { + case 1: + term_bol(); + break; + case 4: + term_delete_char(); + break; + case 5: + term_eol(); + break; + case 9: + term_completion(); + break; + case 10: + case 13: + term_cmd_buf[term_cmd_buf_size] = '\0'; + if (!term_is_password) + term_hist_add(term_cmd_buf); + term_printf("\n"); + /* NOTE: readline_start can be called here */ + term_readline_func(term_readline_opaque, term_cmd_buf); + break; + case 27: + term_esc_state = IS_ESC; + break; + case 127: + case 8: + term_backspace(); + break; + case 155: + term_esc_state = IS_CSI; + break; + default: + if (ch >= 32) { + term_insert_char(ch); + } + break; + } + break; + case IS_ESC: + if (ch == '[') { + term_esc_state = IS_CSI; + term_esc_param = 0; + } else { + term_esc_state = IS_NORM; + } + break; + case IS_CSI: + switch(ch) { + case 'A': + case 'F': + term_up_char(); + break; + case 'B': + case 'E': + term_down_char(); + break; + case 'D': + term_backward_char(); + break; + case 'C': + term_forward_char(); + break; + case '0' ... '9': + term_esc_param = term_esc_param * 10 + (ch - '0'); + goto the_end; + case '~': + switch(term_esc_param) { + case 1: + term_bol(); + break; + case 3: + term_delete_char(); + break; + case 4: + term_eol(); + break; + } + break; + default: + break; + } + term_esc_state = IS_NORM; + the_end: + break; + } + term_update(); +} + +void readline_start(const char *prompt, int is_password, + ReadLineFunc *readline_func, void *opaque) +{ + pstrcpy(term_prompt, sizeof(term_prompt), prompt); + term_readline_func = readline_func; + term_readline_opaque = opaque; + term_is_password = is_password; + term_show_prompt(); +} + +const char *readline_get_history(unsigned int index) +{ + if (index >= TERM_MAX_CMDS) + return NULL; + return term_history[index]; +} + + diff --git a/tools/ioemu/sdl.c b/tools/ioemu/sdl.c new file mode 100644 index 0000000000..311370e480 --- /dev/null +++ b/tools/ioemu/sdl.c @@ -0,0 +1,556 @@ +/* + * QEMU SDL display driver + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#include + +#ifndef _WIN32 +#include +#endif + +static SDL_Surface *screen; +static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ +static int last_vm_running; +static int gui_saved_grab; +static int gui_fullscreen; +static int gui_key_modifier_pressed; +static int gui_keysym; +static int gui_fullscreen_initial_grab; +static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; +static uint8_t modifiers_state[256]; +static int width, height; +static SDL_Cursor *sdl_cursor_normal; +static SDL_Cursor *sdl_cursor_hidden; +static int absolute_enabled = 0; + +static void sdl_update(DisplayState *ds, int x, int y, int w, int h) +{ + // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h); + SDL_UpdateRect(screen, x, y, w, h); +} + +static void sdl_resize(DisplayState *ds, int w, int h) +{ + int flags; + + // printf("resizing to %d %d\n", w, h); + + flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL; + if (gui_fullscreen) + flags |= SDL_FULLSCREEN; + + width = w; + height = h; + + again: + screen = SDL_SetVideoMode(w, h, 0, flags); + if (!screen) { + fprintf(stderr, "Could not open SDL display\n"); + exit(1); + } + if (!screen->pixels && (flags & SDL_HWSURFACE) && (flags & SDL_FULLSCREEN)) { + flags &= ~SDL_HWSURFACE; + goto again; + } + + if (!screen->pixels) { + fprintf(stderr, "Could not open SDL display\n"); + exit(1); + } + ds->data = screen->pixels; + ds->linesize = screen->pitch; + ds->depth = screen->format->BitsPerPixel; + ds->width = w; + ds->height = h; +} + +/* generic keyboard conversion */ + +#include "sdl_keysym.h" +#include "keymaps.c" + +static kbd_layout_t *kbd_layout = NULL; + +static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev) +{ + int keysym; + /* workaround for X11+SDL bug with AltGR */ + keysym = ev->keysym.sym; + if (keysym == 0 && ev->keysym.scancode == 113) + keysym = SDLK_MODE; + return keysym2scancode(kbd_layout, keysym); +} + +/* specific keyboard conversions from scan codes */ + +#if defined(_WIN32) + +static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev) +{ + return ev->keysym.scancode; +} + +#else + +static const uint8_t x_keycode_to_pc_keycode[61] = { + 0xc7, /* 97 Home */ + 0xc8, /* 98 Up */ + 0xc9, /* 99 PgUp */ + 0xcb, /* 100 Left */ + 0x4c, /* 101 KP-5 */ + 0xcd, /* 102 Right */ + 0xcf, /* 103 End */ + 0xd0, /* 104 Down */ + 0xd1, /* 105 PgDn */ + 0xd2, /* 106 Ins */ + 0xd3, /* 107 Del */ + 0x9c, /* 108 Enter */ + 0x9d, /* 109 Ctrl-R */ + 0x0, /* 110 Pause */ + 0xb7, /* 111 Print */ + 0xb5, /* 112 Divide */ + 0xb8, /* 113 Alt-R */ + 0xc6, /* 114 Break */ + 0x0, /* 115 */ + 0x0, /* 116 */ + 0x0, /* 117 */ + 0x0, /* 118 */ + 0x0, /* 119 */ + 0x70, /* 120 Hiragana_Katakana */ + 0x0, /* 121 */ + 0x0, /* 122 */ + 0x73, /* 123 backslash */ + 0x0, /* 124 */ + 0x0, /* 125 */ + 0x0, /* 126 */ + 0x0, /* 127 */ + 0x0, /* 128 */ + 0x79, /* 129 Henkan */ + 0x0, /* 130 */ + 0x7b, /* 131 Muhenkan */ + 0x0, /* 132 */ + 0x7d, /* 133 Yen */ + 0x0, /* 134 */ + 0x0, /* 135 */ + 0x47, /* 136 KP_7 */ + 0x48, /* 137 KP_8 */ + 0x49, /* 138 KP_9 */ + 0x4b, /* 139 KP_4 */ + 0x4c, /* 140 KP_5 */ + 0x4d, /* 141 KP_6 */ + 0x4f, /* 142 KP_1 */ + 0x50, /* 143 KP_2 */ + 0x51, /* 144 KP_3 */ + 0x52, /* 145 KP_0 */ + 0x53, /* 146 KP_. */ + 0x47, /* 147 KP_HOME */ + 0x48, /* 148 KP_UP */ + 0x49, /* 149 KP_PgUp */ + 0x4b, /* 150 KP_Left */ + 0x4c, /* 151 KP_ */ + 0x4d, /* 152 KP_Right */ + 0x4f, /* 153 KP_End */ + 0x50, /* 154 KP_Down */ + 0x51, /* 155 KP_PgDn */ + 0x52, /* 156 KP_Ins */ + 0x53, /* 157 KP_Del */ +}; + +static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev) +{ + int keycode; + + keycode = ev->keysym.scancode; + + if (keycode < 9) { + keycode = 0; + } else if (keycode < 97) { + keycode -= 8; /* just an offset */ + } else if (keycode < 158) { + /* use conversion table */ + keycode = x_keycode_to_pc_keycode[keycode - 97]; + } else { + keycode = 0; + } + return keycode; +} + +#endif + +static void reset_keys(void) +{ + int i; + for(i = 0; i < 256; i++) { + if (modifiers_state[i]) { + if (i & 0x80) + kbd_put_keycode(0xe0); + kbd_put_keycode(i | 0x80); + modifiers_state[i] = 0; + } + } +} + +static void sdl_process_key(SDL_KeyboardEvent *ev) +{ + int keycode, v; + + if (ev->keysym.sym == SDLK_PAUSE) { + /* specific case */ + v = 0; + if (ev->type == SDL_KEYUP) + v |= 0x80; + kbd_put_keycode(0xe1); + kbd_put_keycode(0x1d | v); + kbd_put_keycode(0x45 | v); + return; + } + + if (kbd_layout) { + keycode = sdl_keyevent_to_keycode_generic(ev); + } else { + keycode = sdl_keyevent_to_keycode(ev); + } + + switch(keycode) { + case 0x00: + /* sent when leaving window: reset the modifiers state */ + reset_keys(); + return; + case 0x2a: /* Left Shift */ + case 0x36: /* Right Shift */ + case 0x1d: /* Left CTRL */ + case 0x9d: /* Right CTRL */ + case 0x38: /* Left ALT */ + case 0xb8: /* Right ALT */ + if (ev->type == SDL_KEYUP) + modifiers_state[keycode] = 0; + else + modifiers_state[keycode] = 1; + break; + case 0x45: /* num lock */ + case 0x3a: /* caps lock */ + /* SDL does not send the key up event, so we generate it */ + kbd_put_keycode(keycode); + kbd_put_keycode(keycode | 0x80); + return; + } + + /* now send the key code */ + if (keycode & 0x80) + kbd_put_keycode(0xe0); + if (ev->type == SDL_KEYUP) + kbd_put_keycode(keycode | 0x80); + else + kbd_put_keycode(keycode & 0x7f); +} + +static void sdl_update_caption(void) +{ + char buf[1024]; + strcpy(buf, domain_name); + if (!vm_running) { + strcat(buf, " [Stopped]"); + } + if (gui_grab) { + strcat(buf, " - Press Ctrl-Alt to exit grab"); + } + SDL_WM_SetCaption(buf, domain_name); +} + +static void sdl_hide_cursor(void) +{ + if (kbd_mouse_is_absolute()) { + SDL_ShowCursor(1); + SDL_SetCursor(sdl_cursor_hidden); + } else { + SDL_ShowCursor(0); + } +} + +static void sdl_show_cursor(void) +{ + if (!kbd_mouse_is_absolute()) { + SDL_ShowCursor(1); + } +} + +static void sdl_grab_start(void) +{ + sdl_hide_cursor(); + SDL_WM_GrabInput(SDL_GRAB_ON); + /* dummy read to avoid moving the mouse */ + SDL_GetRelativeMouseState(NULL, NULL); + gui_grab = 1; + sdl_update_caption(); +} + +static void sdl_grab_end(void) +{ + SDL_WM_GrabInput(SDL_GRAB_OFF); + sdl_show_cursor(); + gui_grab = 0; + sdl_update_caption(); +} + +static void sdl_send_mouse_event(int dz) +{ + int dx, dy, state, buttons; + state = SDL_GetRelativeMouseState(&dx, &dy); + buttons = 0; + if (state & SDL_BUTTON(SDL_BUTTON_LEFT)) + buttons |= MOUSE_EVENT_LBUTTON; + if (state & SDL_BUTTON(SDL_BUTTON_RIGHT)) + buttons |= MOUSE_EVENT_RBUTTON; + if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) + buttons |= MOUSE_EVENT_MBUTTON; + + if (kbd_mouse_is_absolute()) { + if (!absolute_enabled) { + sdl_hide_cursor(); + if (gui_grab) { + sdl_grab_end(); + } + absolute_enabled = 1; + } + + SDL_GetMouseState(&dx, &dy); + dx = dx * 0x7FFF / width; + dy = dy * 0x7FFF / height; + } + + kbd_mouse_event(dx, dy, dz, buttons); +} + +static void toggle_full_screen(DisplayState *ds) +{ + gui_fullscreen = !gui_fullscreen; + sdl_resize(ds, screen->w, screen->h); + if (gui_fullscreen) { + gui_saved_grab = gui_grab; + sdl_grab_start(); + } else { + if (!gui_saved_grab) + sdl_grab_end(); + } + vga_hw_invalidate(); + vga_hw_update(); +} + +static void sdl_refresh(DisplayState *ds) +{ + SDL_Event ev1, *ev = &ev1; + int mod_state; + + if (last_vm_running != vm_running) { + last_vm_running = vm_running; + sdl_update_caption(); + } + + vga_hw_update(); + + while (SDL_PollEvent(ev)) { + switch (ev->type) { + case SDL_VIDEOEXPOSE: + sdl_update(ds, 0, 0, screen->w, screen->h); + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + if (ev->type == SDL_KEYDOWN) { + mod_state = (SDL_GetModState() & gui_grab_code) == + gui_grab_code; + gui_key_modifier_pressed = mod_state; + if (gui_key_modifier_pressed) { + int keycode; + keycode = sdl_keyevent_to_keycode(&ev->key); + switch(keycode) { + case 0x21: /* 'f' key on US keyboard */ + toggle_full_screen(ds); + gui_keysym = 1; + break; + case 0x02 ... 0x0a: /* '1' to '9' keys */ + console_select(keycode - 0x02); + if (!is_graphic_console()) { + /* display grab if going to a text console */ + if (gui_grab) + sdl_grab_end(); + } + gui_keysym = 1; + break; + default: + break; + } + } else if (!is_graphic_console()) { + int keysym; + keysym = 0; + if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) { + switch(ev->key.keysym.sym) { + case SDLK_UP: keysym = QEMU_KEY_CTRL_UP; break; + case SDLK_DOWN: keysym = QEMU_KEY_CTRL_DOWN; break; + case SDLK_LEFT: keysym = QEMU_KEY_CTRL_LEFT; break; + case SDLK_RIGHT: keysym = QEMU_KEY_CTRL_RIGHT; break; + case SDLK_HOME: keysym = QEMU_KEY_CTRL_HOME; break; + case SDLK_END: keysym = QEMU_KEY_CTRL_END; break; + case SDLK_PAGEUP: keysym = QEMU_KEY_CTRL_PAGEUP; break; + case SDLK_PAGEDOWN: keysym = QEMU_KEY_CTRL_PAGEDOWN; break; + default: break; + } + } else { + switch(ev->key.keysym.sym) { + case SDLK_UP: keysym = QEMU_KEY_UP; break; + case SDLK_DOWN: keysym = QEMU_KEY_DOWN; break; + case SDLK_LEFT: keysym = QEMU_KEY_LEFT; break; + case SDLK_RIGHT: keysym = QEMU_KEY_RIGHT; break; + case SDLK_HOME: keysym = QEMU_KEY_HOME; break; + case SDLK_END: keysym = QEMU_KEY_END; break; + case SDLK_PAGEUP: keysym = QEMU_KEY_PAGEUP; break; + case SDLK_PAGEDOWN: keysym = QEMU_KEY_PAGEDOWN; break; + case SDLK_BACKSPACE: keysym = QEMU_KEY_BACKSPACE; break; case SDLK_DELETE: keysym = QEMU_KEY_DELETE; break; + default: break; + } + } + if (keysym) { + kbd_put_keysym(keysym); + } else if (ev->key.keysym.unicode != 0) { + kbd_put_keysym(ev->key.keysym.unicode); + } + } + } else if (ev->type == SDL_KEYUP) { + mod_state = (ev->key.keysym.mod & gui_grab_code); + if (!mod_state) { + if (gui_key_modifier_pressed) { + gui_key_modifier_pressed = 0; + if (gui_keysym == 0) { + /* exit/enter grab if pressing Ctrl-Alt */ + if (!gui_grab) + sdl_grab_start(); + else + sdl_grab_end(); + /* SDL does not send back all the + modifiers key, so we must correct it */ + reset_keys(); + break; + } + gui_keysym = 0; + } + } + } + if (is_graphic_console()) + sdl_process_key(&ev->key); + break; + case SDL_QUIT: + qemu_system_shutdown_request(); + break; + case SDL_MOUSEMOTION: + if (gui_grab || kbd_mouse_is_absolute()) { + sdl_send_mouse_event(0); + } + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + { + SDL_MouseButtonEvent *bev = &ev->button; + if (!gui_grab && !kbd_mouse_is_absolute()) { + if (ev->type == SDL_MOUSEBUTTONDOWN && + (bev->state & SDL_BUTTON_LMASK)) { + /* start grabbing all events */ + sdl_grab_start(); + } + } else { + int dz; + dz = 0; +#ifdef SDL_BUTTON_WHEELUP + if (bev->button == SDL_BUTTON_WHEELUP && ev->type == SDL_MOUSEBUTTONDOWN) { + dz = -1; + } else if (bev->button == SDL_BUTTON_WHEELDOWN && ev->type == SDL_MOUSEBUTTONDOWN) { + dz = 1; + } +#endif + sdl_send_mouse_event(dz); + } + } + break; + case SDL_ACTIVEEVENT: + if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS && + !ev->active.gain && !gui_fullscreen_initial_grab) { + sdl_grab_end(); + } + break; + default: + break; + } + } +} + +static void sdl_cleanup(void) +{ + SDL_Quit(); +} + +void sdl_display_init(DisplayState *ds, int full_screen) +{ + int flags; + uint8_t data = 0; + +#if defined(__APPLE__) + /* always use generic keymaps */ + if (!keyboard_layout) + keyboard_layout = "en-us"; +#endif + if(keyboard_layout) { + kbd_layout = init_keyboard_layout(keyboard_layout); + if (!kbd_layout) + exit(1); + } + + flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; + if (SDL_Init (flags)) { + fprintf(stderr, "Could not initialize SDL - exiting\n"); + exit(1); + } +#ifndef _WIN32 + /* NOTE: we still want Ctrl-C to work, so we undo the SDL redirections */ + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); +#endif + + ds->dpy_update = sdl_update; + ds->dpy_resize = sdl_resize; + ds->dpy_refresh = sdl_refresh; + + sdl_resize(ds, 640, 400); + sdl_update_caption(); + SDL_EnableKeyRepeat(250, 50); + SDL_EnableUNICODE(1); + gui_grab = 0; + + sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); + sdl_cursor_normal = SDL_GetCursor(); + + atexit(sdl_cleanup); + if (full_screen) { + gui_fullscreen = 1; + gui_fullscreen_initial_grab = 1; + sdl_grab_start(); + } +} diff --git a/tools/ioemu/sdl_keysym.h b/tools/ioemu/sdl_keysym.h new file mode 100644 index 0000000000..9a7414209f --- /dev/null +++ b/tools/ioemu/sdl_keysym.h @@ -0,0 +1,278 @@ +typedef struct { + const char* name; + int keysym; +} name2keysym_t; +static name2keysym_t name2keysym[]={ +/* ascii */ + { "space", 0x020}, + { "exclam", 0x021}, + { "quotedbl", 0x022}, + { "numbersign", 0x023}, + { "dollar", 0x024}, + { "percent", 0x025}, + { "ampersand", 0x026}, + { "apostrophe", 0x027}, + { "parenleft", 0x028}, + { "parenright", 0x029}, + { "asterisk", 0x02a}, + { "plus", 0x02b}, + { "comma", 0x02c}, + { "minus", 0x02d}, + { "period", 0x02e}, + { "slash", 0x02f}, + { "0", 0x030}, + { "1", 0x031}, + { "2", 0x032}, + { "3", 0x033}, + { "4", 0x034}, + { "5", 0x035}, + { "6", 0x036}, + { "7", 0x037}, + { "8", 0x038}, + { "9", 0x039}, + { "colon", 0x03a}, + { "semicolon", 0x03b}, + { "less", 0x03c}, + { "equal", 0x03d}, + { "greater", 0x03e}, + { "question", 0x03f}, + { "at", 0x040}, + { "A", 0x041}, + { "B", 0x042}, + { "C", 0x043}, + { "D", 0x044}, + { "E", 0x045}, + { "F", 0x046}, + { "G", 0x047}, + { "H", 0x048}, + { "I", 0x049}, + { "J", 0x04a}, + { "K", 0x04b}, + { "L", 0x04c}, + { "M", 0x04d}, + { "N", 0x04e}, + { "O", 0x04f}, + { "P", 0x050}, + { "Q", 0x051}, + { "R", 0x052}, + { "S", 0x053}, + { "T", 0x054}, + { "U", 0x055}, + { "V", 0x056}, + { "W", 0x057}, + { "X", 0x058}, + { "Y", 0x059}, + { "Z", 0x05a}, + { "bracketleft", 0x05b}, + { "backslash", 0x05c}, + { "bracketright", 0x05d}, + { "asciicircum", 0x05e}, + { "underscore", 0x05f}, + { "grave", 0x060}, + { "a", 0x061}, + { "b", 0x062}, + { "c", 0x063}, + { "d", 0x064}, + { "e", 0x065}, + { "f", 0x066}, + { "g", 0x067}, + { "h", 0x068}, + { "i", 0x069}, + { "j", 0x06a}, + { "k", 0x06b}, + { "l", 0x06c}, + { "m", 0x06d}, + { "n", 0x06e}, + { "o", 0x06f}, + { "p", 0x070}, + { "q", 0x071}, + { "r", 0x072}, + { "s", 0x073}, + { "t", 0x074}, + { "u", 0x075}, + { "v", 0x076}, + { "w", 0x077}, + { "x", 0x078}, + { "y", 0x079}, + { "z", 0x07a}, + { "braceleft", 0x07b}, + { "bar", 0x07c}, + { "braceright", 0x07d}, + { "asciitilde", 0x07e}, + +/* latin 1 extensions */ +{ "nobreakspace", 0x0a0}, +{ "exclamdown", 0x0a1}, +{ "cent", 0x0a2}, +{ "sterling", 0x0a3}, +{ "currency", 0x0a4}, +{ "yen", 0x0a5}, +{ "brokenbar", 0x0a6}, +{ "section", 0x0a7}, +{ "diaeresis", 0x0a8}, +{ "copyright", 0x0a9}, +{ "ordfeminine", 0x0aa}, +{ "guillemotleft", 0x0ab}, +{ "notsign", 0x0ac}, +{ "hyphen", 0x0ad}, +{ "registered", 0x0ae}, +{ "macron", 0x0af}, +{ "degree", 0x0b0}, +{ "plusminus", 0x0b1}, +{ "twosuperior", 0x0b2}, +{ "threesuperior", 0x0b3}, +{ "acute", 0x0b4}, +{ "mu", 0x0b5}, +{ "paragraph", 0x0b6}, +{ "periodcentered", 0x0b7}, +{ "cedilla", 0x0b8}, +{ "onesuperior", 0x0b9}, +{ "masculine", 0x0ba}, +{ "guillemotright", 0x0bb}, +{ "onequarter", 0x0bc}, +{ "onehalf", 0x0bd}, +{ "threequarters", 0x0be}, +{ "questiondown", 0x0bf}, +{ "Agrave", 0x0c0}, +{ "Aacute", 0x0c1}, +{ "Acircumflex", 0x0c2}, +{ "Atilde", 0x0c3}, +{ "Adiaeresis", 0x0c4}, +{ "Aring", 0x0c5}, +{ "AE", 0x0c6}, +{ "Ccedilla", 0x0c7}, +{ "Egrave", 0x0c8}, +{ "Eacute", 0x0c9}, +{ "Ecircumflex", 0x0ca}, +{ "Ediaeresis", 0x0cb}, +{ "Igrave", 0x0cc}, +{ "Iacute", 0x0cd}, +{ "Icircumflex", 0x0ce}, +{ "Idiaeresis", 0x0cf}, +{ "ETH", 0x0d0}, +{ "Eth", 0x0d0}, +{ "Ntilde", 0x0d1}, +{ "Ograve", 0x0d2}, +{ "Oacute", 0x0d3}, +{ "Ocircumflex", 0x0d4}, +{ "Otilde", 0x0d5}, +{ "Odiaeresis", 0x0d6}, +{ "multiply", 0x0d7}, +{ "Ooblique", 0x0d8}, +{ "Oslash", 0x0d8}, +{ "Ugrave", 0x0d9}, +{ "Uacute", 0x0da}, +{ "Ucircumflex", 0x0db}, +{ "Udiaeresis", 0x0dc}, +{ "Yacute", 0x0dd}, +{ "THORN", 0x0de}, +{ "Thorn", 0x0de}, +{ "ssharp", 0x0df}, +{ "agrave", 0x0e0}, +{ "aacute", 0x0e1}, +{ "acircumflex", 0x0e2}, +{ "atilde", 0x0e3}, +{ "adiaeresis", 0x0e4}, +{ "aring", 0x0e5}, +{ "ae", 0x0e6}, +{ "ccedilla", 0x0e7}, +{ "egrave", 0x0e8}, +{ "eacute", 0x0e9}, +{ "ecircumflex", 0x0ea}, +{ "ediaeresis", 0x0eb}, +{ "igrave", 0x0ec}, +{ "iacute", 0x0ed}, +{ "icircumflex", 0x0ee}, +{ "idiaeresis", 0x0ef}, +{ "eth", 0x0f0}, +{ "ntilde", 0x0f1}, +{ "ograve", 0x0f2}, +{ "oacute", 0x0f3}, +{ "ocircumflex", 0x0f4}, +{ "otilde", 0x0f5}, +{ "odiaeresis", 0x0f6}, +{ "division", 0x0f7}, +{ "oslash", 0x0f8}, +{ "ooblique", 0x0f8}, +{ "ugrave", 0x0f9}, +{ "uacute", 0x0fa}, +{ "ucircumflex", 0x0fb}, +{ "udiaeresis", 0x0fc}, +{ "yacute", 0x0fd}, +{ "thorn", 0x0fe}, +{ "ydiaeresis", 0x0ff}, +{"EuroSign", SDLK_EURO}, + + /* modifiers */ +{"Control_L", SDLK_LCTRL}, +{"Control_R", SDLK_RCTRL}, +{"Alt_L", SDLK_LALT}, +{"Alt_R", SDLK_RALT}, +{"Caps_Lock", SDLK_CAPSLOCK}, +{"Meta_L", SDLK_LMETA}, +{"Meta_R", SDLK_RMETA}, +{"Shift_L", SDLK_LSHIFT}, +{"Shift_R", SDLK_RSHIFT}, +{"Super_L", SDLK_LSUPER}, +{"Super_R", SDLK_RSUPER}, + + /* special keys */ +{"BackSpace", SDLK_BACKSPACE}, +{"Tab", SDLK_TAB}, +{"Return", SDLK_RETURN}, +{"Right", SDLK_RIGHT}, +{"Left", SDLK_LEFT}, +{"Up", SDLK_UP}, +{"Down", SDLK_DOWN}, +{"Page_Down", SDLK_PAGEDOWN}, +{"Page_Up", SDLK_PAGEUP}, +{"Insert", SDLK_INSERT}, +{"Delete", SDLK_DELETE}, +{"Home", SDLK_HOME}, +{"End", SDLK_END}, +{"Scroll_Lock", SDLK_SCROLLOCK}, +{"F1", SDLK_F1}, +{"F2", SDLK_F2}, +{"F3", SDLK_F3}, +{"F4", SDLK_F4}, +{"F5", SDLK_F5}, +{"F6", SDLK_F6}, +{"F7", SDLK_F7}, +{"F8", SDLK_F8}, +{"F9", SDLK_F9}, +{"F10", SDLK_F10}, +{"F11", SDLK_F11}, +{"F12", SDLK_F12}, +{"F13", SDLK_F13}, +{"F14", SDLK_F14}, +{"F15", SDLK_F15}, +{"Sys_Req", SDLK_SYSREQ}, +{"KP_0", SDLK_KP0}, +{"KP_1", SDLK_KP1}, +{"KP_2", SDLK_KP2}, +{"KP_3", SDLK_KP3}, +{"KP_4", SDLK_KP4}, +{"KP_5", SDLK_KP5}, +{"KP_6", SDLK_KP6}, +{"KP_7", SDLK_KP7}, +{"KP_8", SDLK_KP8}, +{"KP_9", SDLK_KP9}, +{"KP_Add", SDLK_KP_PLUS}, +{"KP_Decimal", SDLK_KP_PERIOD}, +{"KP_Divide", SDLK_KP_DIVIDE}, +{"KP_Enter", SDLK_KP_ENTER}, +{"KP_Equal", SDLK_KP_EQUALS}, +{"KP_Multiply", SDLK_KP_MULTIPLY}, +{"KP_Subtract", SDLK_KP_MINUS}, +{"help", SDLK_HELP}, +{"Menu", SDLK_MENU}, +{"Power", SDLK_POWER}, +{"Print", SDLK_PRINT}, +{"Mode_switch", SDLK_MODE}, +{"Multi_Key", SDLK_COMPOSE}, +{"Num_Lock", SDLK_NUMLOCK}, +{"Pause", SDLK_PAUSE}, +{"Escape", SDLK_ESCAPE}, + +{0,0}, +}; diff --git a/tools/ioemu/softmmu_exec.h b/tools/ioemu/softmmu_exec.h new file mode 100644 index 0000000000..3b789eeb75 --- /dev/null +++ b/tools/ioemu/softmmu_exec.h @@ -0,0 +1,65 @@ +/* Common softmmu definitions and inline routines. */ + +#define ldul_user ldl_user +#define ldul_kernel ldl_kernel + +#define ACCESS_TYPE 0 +#define MEMSUFFIX _kernel +#define DATA_SIZE 1 +#include "softmmu_header.h" + +#define DATA_SIZE 2 +#include "softmmu_header.h" + +#define DATA_SIZE 4 +#include "softmmu_header.h" + +#define DATA_SIZE 8 +#include "softmmu_header.h" +#undef ACCESS_TYPE +#undef MEMSUFFIX + +#define ACCESS_TYPE 1 +#define MEMSUFFIX _user +#define DATA_SIZE 1 +#include "softmmu_header.h" + +#define DATA_SIZE 2 +#include "softmmu_header.h" + +#define DATA_SIZE 4 +#include "softmmu_header.h" + +#define DATA_SIZE 8 +#include "softmmu_header.h" +#undef ACCESS_TYPE +#undef MEMSUFFIX + +/* these access are slower, they must be as rare as possible */ +#define ACCESS_TYPE 2 +#define MEMSUFFIX _data +#define DATA_SIZE 1 +#include "softmmu_header.h" + +#define DATA_SIZE 2 +#include "softmmu_header.h" + +#define DATA_SIZE 4 +#include "softmmu_header.h" + +#define DATA_SIZE 8 +#include "softmmu_header.h" +#undef ACCESS_TYPE +#undef MEMSUFFIX + +#define ldub(p) ldub_data(p) +#define ldsb(p) ldsb_data(p) +#define lduw(p) lduw_data(p) +#define ldsw(p) ldsw_data(p) +#define ldl(p) ldl_data(p) +#define ldq(p) ldq_data(p) + +#define stb(p, v) stb_data(p, v) +#define stw(p, v) stw_data(p, v) +#define stl(p, v) stl_data(p, v) +#define stq(p, v) stq_data(p, v) diff --git a/tools/ioemu/softmmu_header.h b/tools/ioemu/softmmu_header.h new file mode 100644 index 0000000000..d5b3debc74 --- /dev/null +++ b/tools/ioemu/softmmu_header.h @@ -0,0 +1,385 @@ +/* + * Software MMU support + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#if DATA_SIZE == 8 +#define SUFFIX q +#define USUFFIX q +#define DATA_TYPE uint64_t +#elif DATA_SIZE == 4 +#define SUFFIX l +#define USUFFIX l +#define DATA_TYPE uint32_t +#elif DATA_SIZE == 2 +#define SUFFIX w +#define USUFFIX uw +#define DATA_TYPE uint16_t +#define DATA_STYPE int16_t +#elif DATA_SIZE == 1 +#define SUFFIX b +#define USUFFIX ub +#define DATA_TYPE uint8_t +#define DATA_STYPE int8_t +#else +#error unsupported data size +#endif + +#if ACCESS_TYPE == 0 + +#define CPU_MEM_INDEX 0 +#define MMUSUFFIX _mmu + +#elif ACCESS_TYPE == 1 + +#define CPU_MEM_INDEX 1 +#define MMUSUFFIX _mmu + +#elif ACCESS_TYPE == 2 + +#ifdef TARGET_I386 +#define CPU_MEM_INDEX ((env->hflags & HF_CPL_MASK) == 3) +#elif defined (TARGET_PPC) +#define CPU_MEM_INDEX (msr_pr) +#elif defined (TARGET_MIPS) +#define CPU_MEM_INDEX ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM) +#elif defined (TARGET_SPARC) +#define CPU_MEM_INDEX ((env->psrs) == 0) +#elif defined (TARGET_ARM) +#define CPU_MEM_INDEX ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) +#elif defined (TARGET_SH4) +#define CPU_MEM_INDEX ((env->sr & SR_MD) == 0) +#else +#error unsupported CPU +#endif +#define MMUSUFFIX _mmu + +#elif ACCESS_TYPE == 3 + +#ifdef TARGET_I386 +#define CPU_MEM_INDEX ((env->hflags & HF_CPL_MASK) == 3) +#elif defined (TARGET_PPC) +#define CPU_MEM_INDEX (msr_pr) +#elif defined (TARGET_MIPS) +#define CPU_MEM_INDEX ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM) +#elif defined (TARGET_SPARC) +#define CPU_MEM_INDEX ((env->psrs) == 0) +#elif defined (TARGET_ARM) +#define CPU_MEM_INDEX ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) +#elif defined (TARGET_SH4) +#define CPU_MEM_INDEX ((env->sr & SR_MD) == 0) +#else +#error unsupported CPU +#endif +#define MMUSUFFIX _cmmu + +#else +#error invalid ACCESS_TYPE +#endif + +#if DATA_SIZE == 8 +#define RES_TYPE uint64_t +#else +#define RES_TYPE int +#endif + +#if ACCESS_TYPE == 3 +#define ADDR_READ addr_code +#else +#define ADDR_READ addr_read +#endif + +DATA_TYPE REGPARM(1) glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr, + int is_user); +void REGPARM(2) glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, DATA_TYPE v, int is_user); + +#if (DATA_SIZE <= 4) && (TARGET_LONG_BITS == 32) && defined(__i386__) && \ + (ACCESS_TYPE <= 1) && defined(ASM_SOFTMMU) + +#define CPU_TLB_ENTRY_BITS 4 + +static inline RES_TYPE glue(glue(ld, USUFFIX), MEMSUFFIX)(target_ulong ptr) +{ + int res; + + asm volatile ("movl %1, %%edx\n" + "movl %1, %%eax\n" + "shrl %3, %%edx\n" + "andl %4, %%eax\n" + "andl %2, %%edx\n" + "leal %5(%%edx, %%ebp), %%edx\n" + "cmpl (%%edx), %%eax\n" + "movl %1, %%eax\n" + "je 1f\n" + "pushl %6\n" + "call %7\n" + "popl %%edx\n" + "movl %%eax, %0\n" + "jmp 2f\n" + "1:\n" + "addl 12(%%edx), %%eax\n" +#if DATA_SIZE == 1 + "movzbl (%%eax), %0\n" +#elif DATA_SIZE == 2 + "movzwl (%%eax), %0\n" +#elif DATA_SIZE == 4 + "movl (%%eax), %0\n" +#else +#error unsupported size +#endif + "2:\n" + : "=r" (res) + : "r" (ptr), + "i" ((CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS), + "i" (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS), + "i" (TARGET_PAGE_MASK | (DATA_SIZE - 1)), + "m" (*(uint32_t *)offsetof(CPUState, tlb_table[CPU_MEM_INDEX][0].addr_read)), + "i" (CPU_MEM_INDEX), + "m" (*(uint8_t *)&glue(glue(__ld, SUFFIX), MMUSUFFIX)) + : "%eax", "%ecx", "%edx", "memory", "cc"); + return res; +} + +#if DATA_SIZE <= 2 +static inline int glue(glue(lds, SUFFIX), MEMSUFFIX)(target_ulong ptr) +{ + int res; + + asm volatile ("movl %1, %%edx\n" + "movl %1, %%eax\n" + "shrl %3, %%edx\n" + "andl %4, %%eax\n" + "andl %2, %%edx\n" + "leal %5(%%edx, %%ebp), %%edx\n" + "cmpl (%%edx), %%eax\n" + "movl %1, %%eax\n" + "je 1f\n" + "pushl %6\n" + "call %7\n" + "popl %%edx\n" +#if DATA_SIZE == 1 + "movsbl %%al, %0\n" +#elif DATA_SIZE == 2 + "movswl %%ax, %0\n" +#else +#error unsupported size +#endif + "jmp 2f\n" + "1:\n" + "addl 12(%%edx), %%eax\n" +#if DATA_SIZE == 1 + "movsbl (%%eax), %0\n" +#elif DATA_SIZE == 2 + "movswl (%%eax), %0\n" +#else +#error unsupported size +#endif + "2:\n" + : "=r" (res) + : "r" (ptr), + "i" ((CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS), + "i" (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS), + "i" (TARGET_PAGE_MASK | (DATA_SIZE - 1)), + "m" (*(uint32_t *)offsetof(CPUState, tlb_table[CPU_MEM_INDEX][0].addr_read)), + "i" (CPU_MEM_INDEX), + "m" (*(uint8_t *)&glue(glue(__ld, SUFFIX), MMUSUFFIX)) + : "%eax", "%ecx", "%edx", "memory", "cc"); + return res; +} +#endif + +static inline void glue(glue(st, SUFFIX), MEMSUFFIX)(target_ulong ptr, RES_TYPE v) +{ + asm volatile ("movl %0, %%edx\n" + "movl %0, %%eax\n" + "shrl %3, %%edx\n" + "andl %4, %%eax\n" + "andl %2, %%edx\n" + "leal %5(%%edx, %%ebp), %%edx\n" + "cmpl (%%edx), %%eax\n" + "movl %0, %%eax\n" + "je 1f\n" +#if DATA_SIZE == 1 + "movzbl %b1, %%edx\n" +#elif DATA_SIZE == 2 + "movzwl %w1, %%edx\n" +#elif DATA_SIZE == 4 + "movl %1, %%edx\n" +#else +#error unsupported size +#endif + "pushl %6\n" + "call %7\n" + "popl %%eax\n" + "jmp 2f\n" + "1:\n" + "addl 8(%%edx), %%eax\n" +#if DATA_SIZE == 1 + "movb %b1, (%%eax)\n" +#elif DATA_SIZE == 2 + "movw %w1, (%%eax)\n" +#elif DATA_SIZE == 4 + "movl %1, (%%eax)\n" +#else +#error unsupported size +#endif + "2:\n" + : + : "r" (ptr), +/* NOTE: 'q' would be needed as constraint, but we could not use it + with T1 ! */ + "r" (v), + "i" ((CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS), + "i" (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS), + "i" (TARGET_PAGE_MASK | (DATA_SIZE - 1)), + "m" (*(uint32_t *)offsetof(CPUState, tlb_table[CPU_MEM_INDEX][0].addr_write)), + "i" (CPU_MEM_INDEX), + "m" (*(uint8_t *)&glue(glue(__st, SUFFIX), MMUSUFFIX)) + : "%eax", "%ecx", "%edx", "memory", "cc"); +} + +#else + +/* generic load/store macros */ + +static inline RES_TYPE glue(glue(ld, USUFFIX), MEMSUFFIX)(target_ulong ptr) +{ + int index; + RES_TYPE res; + target_ulong addr; + unsigned long physaddr; + int is_user; + + addr = ptr; + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + is_user = CPU_MEM_INDEX; + if (__builtin_expect(env->tlb_table[is_user][index].ADDR_READ != + (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))), 0)) { + res = glue(glue(__ld, SUFFIX), MMUSUFFIX)(addr, is_user); + } else { + physaddr = addr + env->tlb_table[is_user][index].addend; + res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)physaddr); + } + return res; +} + +#if DATA_SIZE <= 2 +static inline int glue(glue(lds, SUFFIX), MEMSUFFIX)(target_ulong ptr) +{ + int res, index; + target_ulong addr; + unsigned long physaddr; + int is_user; + + addr = ptr; + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + is_user = CPU_MEM_INDEX; + if (__builtin_expect(env->tlb_table[is_user][index].ADDR_READ != + (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))), 0)) { + res = (DATA_STYPE)glue(glue(__ld, SUFFIX), MMUSUFFIX)(addr, is_user); + } else { + physaddr = addr + env->tlb_table[is_user][index].addend; + res = glue(glue(lds, SUFFIX), _raw)((uint8_t *)physaddr); + } + return res; +} +#endif + +#if ACCESS_TYPE != 3 + +/* generic store macro */ + +static inline void glue(glue(st, SUFFIX), MEMSUFFIX)(target_ulong ptr, RES_TYPE v) +{ + int index; + target_ulong addr; + unsigned long physaddr; + int is_user; + + addr = ptr; + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + is_user = CPU_MEM_INDEX; + if (__builtin_expect(env->tlb_table[is_user][index].addr_write != + (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))), 0)) { + glue(glue(__st, SUFFIX), MMUSUFFIX)(addr, v, is_user); + } else { + physaddr = addr + env->tlb_table[is_user][index].addend; + glue(glue(st, SUFFIX), _raw)((uint8_t *)physaddr, v); + } +} + +#endif /* ACCESS_TYPE != 3 */ + +#endif /* !asm */ + +#if ACCESS_TYPE != 3 + +#if DATA_SIZE == 8 +static inline float64 glue(ldfq, MEMSUFFIX)(target_ulong ptr) +{ + union { + float64 d; + uint64_t i; + } u; + u.i = glue(ldq, MEMSUFFIX)(ptr); + return u.d; +} + +static inline void glue(stfq, MEMSUFFIX)(target_ulong ptr, float64 v) +{ + union { + float64 d; + uint64_t i; + } u; + u.d = v; + glue(stq, MEMSUFFIX)(ptr, u.i); +} +#endif /* DATA_SIZE == 8 */ + +#if DATA_SIZE == 4 +static inline float32 glue(ldfl, MEMSUFFIX)(target_ulong ptr) +{ + union { + float32 f; + uint32_t i; + } u; + u.i = glue(ldl, MEMSUFFIX)(ptr); + return u.f; +} + +static inline void glue(stfl, MEMSUFFIX)(target_ulong ptr, float32 v) +{ + union { + float32 f; + uint32_t i; + } u; + u.f = v; + glue(stl, MEMSUFFIX)(ptr, u.i); +} +#endif /* DATA_SIZE == 4 */ + +#endif /* ACCESS_TYPE != 3 */ + +#undef RES_TYPE +#undef DATA_TYPE +#undef DATA_STYPE +#undef SUFFIX +#undef USUFFIX +#undef DATA_SIZE +#undef CPU_MEM_INDEX +#undef MMUSUFFIX +#undef ADDR_READ diff --git a/tools/ioemu/softmmu_template.h b/tools/ioemu/softmmu_template.h new file mode 100644 index 0000000000..1c12c42411 --- /dev/null +++ b/tools/ioemu/softmmu_template.h @@ -0,0 +1,313 @@ +/* + * Software MMU support + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#define DATA_SIZE (1 << SHIFT) + +#if DATA_SIZE == 8 +#define SUFFIX q +#define USUFFIX q +#define DATA_TYPE uint64_t +#elif DATA_SIZE == 4 +#define SUFFIX l +#define USUFFIX l +#define DATA_TYPE uint32_t +#elif DATA_SIZE == 2 +#define SUFFIX w +#define USUFFIX uw +#define DATA_TYPE uint16_t +#elif DATA_SIZE == 1 +#define SUFFIX b +#define USUFFIX ub +#define DATA_TYPE uint8_t +#else +#error unsupported data size +#endif + +#ifdef SOFTMMU_CODE_ACCESS +#define READ_ACCESS_TYPE 2 +#define ADDR_READ addr_code +#else +#define READ_ACCESS_TYPE 0 +#define ADDR_READ addr_read +#endif + +static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr, + int is_user, + void *retaddr); +static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr, + target_ulong tlb_addr) +{ + DATA_TYPE res; + int index; + + index = (tlb_addr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); +#if SHIFT <= 2 + res = io_mem_read[index][SHIFT](io_mem_opaque[index], physaddr); +#else +#ifdef TARGET_WORDS_BIGENDIAN + res = (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr) << 32; + res |= io_mem_read[index][2](io_mem_opaque[index], physaddr + 4); +#else + res = io_mem_read[index][2](io_mem_opaque[index], physaddr); + res |= (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr + 4) << 32; +#endif +#endif /* SHIFT > 2 */ +#ifdef USE_KQEMU + env->last_io_time = cpu_get_time_fast(); +#endif + return res; +} + +/* handle all cases except unaligned access which span two pages */ +DATA_TYPE REGPARM(1) glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr, + int is_user) +{ + DATA_TYPE res; + int index; + target_ulong tlb_addr; + target_phys_addr_t physaddr; + void *retaddr; + + /* test if there is match for unaligned or IO access */ + /* XXX: could done more in memory macro in a non portable way */ + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + redo: + tlb_addr = env->tlb_table[is_user][index].ADDR_READ; + if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + physaddr = addr + env->tlb_table[is_user][index].addend; + if (tlb_addr & ~TARGET_PAGE_MASK) { + /* IO access */ + if ((addr & (DATA_SIZE - 1)) != 0) + goto do_unaligned_access; + res = glue(io_read, SUFFIX)(physaddr, tlb_addr); + } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { + /* slow unaligned access (it spans two pages or IO) */ + do_unaligned_access: + retaddr = GETPC(); +#ifdef ALIGNED_ONLY + do_unaligned_access(addr, READ_ACCESS_TYPE, is_user, retaddr); +#endif + res = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr, + is_user, retaddr); + } else { + /* unaligned/aligned access in the same page */ +#ifdef ALIGNED_ONLY + if ((addr & (DATA_SIZE - 1)) != 0) { + retaddr = GETPC(); + do_unaligned_access(addr, READ_ACCESS_TYPE, is_user, retaddr); + } +#endif + res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)physaddr); + } + } else { + /* the page is not in the TLB : fill it */ + retaddr = GETPC(); +#ifdef ALIGNED_ONLY + if ((addr & (DATA_SIZE - 1)) != 0) + do_unaligned_access(addr, READ_ACCESS_TYPE, is_user, retaddr); +#endif + tlb_fill(addr, READ_ACCESS_TYPE, is_user, retaddr); + goto redo; + } + return res; +} + +/* handle all unaligned cases */ +static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr, + int is_user, + void *retaddr) +{ + DATA_TYPE res, res1, res2; + int index, shift; + target_phys_addr_t physaddr; + target_ulong tlb_addr, addr1, addr2; + + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + redo: + tlb_addr = env->tlb_table[is_user][index].ADDR_READ; + if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + physaddr = addr + env->tlb_table[is_user][index].addend; + if (tlb_addr & ~TARGET_PAGE_MASK) { + /* IO access */ + if ((addr & (DATA_SIZE - 1)) != 0) + goto do_unaligned_access; + res = glue(io_read, SUFFIX)(physaddr, tlb_addr); + } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { + do_unaligned_access: + /* slow unaligned access (it spans two pages) */ + addr1 = addr & ~(DATA_SIZE - 1); + addr2 = addr1 + DATA_SIZE; + res1 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr1, + is_user, retaddr); + res2 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr2, + is_user, retaddr); + shift = (addr & (DATA_SIZE - 1)) * 8; +#ifdef TARGET_WORDS_BIGENDIAN + res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift)); +#else + res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift)); +#endif + res = (DATA_TYPE)res; + } else { + /* unaligned/aligned access in the same page */ + res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)physaddr); + } + } else { + /* the page is not in the TLB : fill it */ + tlb_fill(addr, READ_ACCESS_TYPE, is_user, retaddr); + goto redo; + } + return res; +} + +#ifndef SOFTMMU_CODE_ACCESS + +static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr, + DATA_TYPE val, + int is_user, + void *retaddr); + +static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr, + DATA_TYPE val, + target_ulong tlb_addr, + void *retaddr) +{ + int index; + + index = (tlb_addr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + env->mem_write_vaddr = tlb_addr; + env->mem_write_pc = (unsigned long)retaddr; +#if SHIFT <= 2 + io_mem_write[index][SHIFT](io_mem_opaque[index], physaddr, val); +#else +#ifdef TARGET_WORDS_BIGENDIAN + io_mem_write[index][2](io_mem_opaque[index], physaddr, val >> 32); + io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val); +#else + io_mem_write[index][2](io_mem_opaque[index], physaddr, val); + io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val >> 32); +#endif +#endif /* SHIFT > 2 */ +#ifdef USE_KQEMU + env->last_io_time = cpu_get_time_fast(); +#endif +} + +void REGPARM(2) glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, + DATA_TYPE val, + int is_user) +{ + target_phys_addr_t physaddr; + target_ulong tlb_addr; + void *retaddr; + int index; + + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + redo: + tlb_addr = env->tlb_table[is_user][index].addr_write; + if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + physaddr = addr + env->tlb_table[is_user][index].addend; + if (tlb_addr & ~TARGET_PAGE_MASK) { + /* IO access */ + if ((addr & (DATA_SIZE - 1)) != 0) + goto do_unaligned_access; + retaddr = GETPC(); + glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr); + } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { + do_unaligned_access: + retaddr = GETPC(); +#ifdef ALIGNED_ONLY + do_unaligned_access(addr, 1, is_user, retaddr); +#endif + glue(glue(slow_st, SUFFIX), MMUSUFFIX)(addr, val, + is_user, retaddr); + } else { + /* aligned/unaligned access in the same page */ +#ifdef ALIGNED_ONLY + if ((addr & (DATA_SIZE - 1)) != 0) { + retaddr = GETPC(); + do_unaligned_access(addr, 1, is_user, retaddr); + } +#endif + glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)physaddr, val); + } + } else { + /* the page is not in the TLB : fill it */ + retaddr = GETPC(); +#ifdef ALIGNED_ONLY + if ((addr & (DATA_SIZE - 1)) != 0) + do_unaligned_access(addr, 1, is_user, retaddr); +#endif + tlb_fill(addr, 1, is_user, retaddr); + goto redo; + } +} + +/* handles all unaligned cases */ +static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr, + DATA_TYPE val, + int is_user, + void *retaddr) +{ + target_phys_addr_t physaddr; + target_ulong tlb_addr; + int index, i; + + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + redo: + tlb_addr = env->tlb_table[is_user][index].addr_write; + if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + physaddr = addr + env->tlb_table[is_user][index].addend; + if (tlb_addr & ~TARGET_PAGE_MASK) { + /* IO access */ + if ((addr & (DATA_SIZE - 1)) != 0) + goto do_unaligned_access; + glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr); + } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { + do_unaligned_access: + /* XXX: not efficient, but simple */ + for(i = 0;i < DATA_SIZE; i++) { +#ifdef TARGET_WORDS_BIGENDIAN + glue(slow_stb, MMUSUFFIX)(addr + i, val >> (((DATA_SIZE - 1) * 8) - (i * 8)), + is_user, retaddr); +#else + glue(slow_stb, MMUSUFFIX)(addr + i, val >> (i * 8), + is_user, retaddr); +#endif + } + } else { + /* aligned/unaligned access in the same page */ + glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)physaddr, val); + } + } else { + /* the page is not in the TLB : fill it */ + tlb_fill(addr, 1, is_user, retaddr); + goto redo; + } +} + +#endif /* !defined(SOFTMMU_CODE_ACCESS) */ + +#undef READ_ACCESS_TYPE +#undef SHIFT +#undef DATA_TYPE +#undef SUFFIX +#undef USUFFIX +#undef DATA_SIZE +#undef ADDR_READ diff --git a/tools/ioemu/tap-win32.c b/tools/ioemu/tap-win32.c new file mode 100644 index 0000000000..b6056955c0 --- /dev/null +++ b/tools/ioemu/tap-win32.c @@ -0,0 +1,680 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) James Yonan, 2003-2004, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "vl.h" +#include +#include + +/* NOTE: PCIBus is redefined in winddk.h */ +#define PCIBus _PCIBus +#include +#include +#include +#undef PCIBus + +//============= +// TAP IOCTLs +//============= + +#define TAP_CONTROL_CODE(request,method) \ + CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) + +#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED) +#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED) +#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED) +#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED) +#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED) +#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED) + +//================= +// Registry keys +//================= + +#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" + +#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" + +//====================== +// Filesystem prefixes +//====================== + +#define USERMODEDEVICEDIR "\\\\.\\Global\\" +#define TAPSUFFIX ".tap" + + +//====================== +// Compile time configuration +//====================== + +//#define DEBUG_TAP_WIN32 1 + +#define TUN_ASYNCHRONOUS_WRITES 1 + +#define TUN_BUFFER_SIZE 1560 +#define TUN_MAX_BUFFER_COUNT 32 + +/* + * The data member "buffer" must be the first element in the tun_buffer + * structure. See the function, tap_win32_free_buffer. + */ +typedef struct tun_buffer_s { + unsigned char buffer [TUN_BUFFER_SIZE]; + unsigned long read_size; + struct tun_buffer_s* next; +} tun_buffer_t; + +typedef struct tap_win32_overlapped { + HANDLE handle; + HANDLE read_event; + HANDLE write_event; + HANDLE output_queue_semaphore; + HANDLE free_list_semaphore; + CRITICAL_SECTION output_queue_cs; + CRITICAL_SECTION free_list_cs; + OVERLAPPED read_overlapped; + OVERLAPPED write_overlapped; + tun_buffer_t buffers[TUN_MAX_BUFFER_COUNT]; + tun_buffer_t* free_list; + tun_buffer_t* output_queue_front; + tun_buffer_t* output_queue_back; +} tap_win32_overlapped_t; + +static tap_win32_overlapped_t tap_overlapped; + +static tun_buffer_t* get_buffer_from_free_list(tap_win32_overlapped_t* const overlapped) +{ + tun_buffer_t* buffer = NULL; + WaitForSingleObject(overlapped->free_list_semaphore, INFINITE); + EnterCriticalSection(&overlapped->free_list_cs); + buffer = overlapped->free_list; +// assert(buffer != NULL); + overlapped->free_list = buffer->next; + LeaveCriticalSection(&overlapped->free_list_cs); + buffer->next = NULL; + return buffer; +} + +static void put_buffer_on_free_list(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer) +{ + EnterCriticalSection(&overlapped->free_list_cs); + buffer->next = overlapped->free_list; + overlapped->free_list = buffer; + LeaveCriticalSection(&overlapped->free_list_cs); + ReleaseSemaphore(overlapped->free_list_semaphore, 1, NULL); +} + +static tun_buffer_t* get_buffer_from_output_queue(tap_win32_overlapped_t* const overlapped, const int block) +{ + tun_buffer_t* buffer = NULL; + DWORD result, timeout = block ? INFINITE : 0L; + + // Non-blocking call + result = WaitForSingleObject(overlapped->output_queue_semaphore, timeout); + + switch (result) + { + // The semaphore object was signaled. + case WAIT_OBJECT_0: + EnterCriticalSection(&overlapped->output_queue_cs); + + buffer = overlapped->output_queue_front; + overlapped->output_queue_front = buffer->next; + + if(overlapped->output_queue_front == NULL) { + overlapped->output_queue_back = NULL; + } + + LeaveCriticalSection(&overlapped->output_queue_cs); + break; + + // Semaphore was nonsignaled, so a time-out occurred. + case WAIT_TIMEOUT: + // Cannot open another window. + break; + } + + return buffer; +} + +static tun_buffer_t* get_buffer_from_output_queue_immediate (tap_win32_overlapped_t* const overlapped) +{ + return get_buffer_from_output_queue(overlapped, 0); +} + +static void put_buffer_on_output_queue(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer) +{ + EnterCriticalSection(&overlapped->output_queue_cs); + + if(overlapped->output_queue_front == NULL && overlapped->output_queue_back == NULL) { + overlapped->output_queue_front = overlapped->output_queue_back = buffer; + } else { + buffer->next = NULL; + overlapped->output_queue_back->next = buffer; + overlapped->output_queue_back = buffer; + } + + LeaveCriticalSection(&overlapped->output_queue_cs); + + ReleaseSemaphore(overlapped->output_queue_semaphore, 1, NULL); +} + + +static int is_tap_win32_dev(const char *guid) +{ + HKEY netcard_key; + LONG status; + DWORD len; + int i = 0; + + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + ADAPTER_KEY, + 0, + KEY_READ, + &netcard_key); + + if (status != ERROR_SUCCESS) { + return FALSE; + } + + for (;;) { + char enum_name[256]; + char unit_string[256]; + HKEY unit_key; + char component_id_string[] = "ComponentId"; + char component_id[256]; + char net_cfg_instance_id_string[] = "NetCfgInstanceId"; + char net_cfg_instance_id[256]; + DWORD data_type; + + len = sizeof (enum_name); + status = RegEnumKeyEx( + netcard_key, + i, + enum_name, + &len, + NULL, + NULL, + NULL, + NULL); + + if (status == ERROR_NO_MORE_ITEMS) + break; + else if (status != ERROR_SUCCESS) { + return FALSE; + } + + snprintf (unit_string, sizeof(unit_string), "%s\\%s", + ADAPTER_KEY, enum_name); + + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + unit_string, + 0, + KEY_READ, + &unit_key); + + if (status != ERROR_SUCCESS) { + return FALSE; + } else { + len = sizeof (component_id); + status = RegQueryValueEx( + unit_key, + component_id_string, + NULL, + &data_type, + component_id, + &len); + + if (!(status != ERROR_SUCCESS || data_type != REG_SZ)) { + len = sizeof (net_cfg_instance_id); + status = RegQueryValueEx( + unit_key, + net_cfg_instance_id_string, + NULL, + &data_type, + net_cfg_instance_id, + &len); + + if (status == ERROR_SUCCESS && data_type == REG_SZ) { + if (/* !strcmp (component_id, TAP_COMPONENT_ID) &&*/ + !strcmp (net_cfg_instance_id, guid)) { + RegCloseKey (unit_key); + RegCloseKey (netcard_key); + return TRUE; + } + } + } + RegCloseKey (unit_key); + } + ++i; + } + + RegCloseKey (netcard_key); + return FALSE; +} + +static int get_device_guid( + char *name, + int name_size, + char *actual_name, + int actual_name_size) +{ + LONG status; + HKEY control_net_key; + DWORD len; + int i = 0; + int stop = 0; + + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + NETWORK_CONNECTIONS_KEY, + 0, + KEY_READ, + &control_net_key); + + if (status != ERROR_SUCCESS) { + return -1; + } + + while (!stop) + { + char enum_name[256]; + char connection_string[256]; + HKEY connection_key; + char name_data[256]; + DWORD name_type; + const char name_string[] = "Name"; + + len = sizeof (enum_name); + status = RegEnumKeyEx( + control_net_key, + i, + enum_name, + &len, + NULL, + NULL, + NULL, + NULL); + + if (status == ERROR_NO_MORE_ITEMS) + break; + else if (status != ERROR_SUCCESS) { + return -1; + } + + snprintf(connection_string, + sizeof(connection_string), + "%s\\%s\\Connection", + NETWORK_CONNECTIONS_KEY, enum_name); + + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + connection_string, + 0, + KEY_READ, + &connection_key); + + if (status == ERROR_SUCCESS) { + len = sizeof (name_data); + status = RegQueryValueEx( + connection_key, + name_string, + NULL, + &name_type, + name_data, + &len); + + if (status != ERROR_SUCCESS || name_type != REG_SZ) { + return -1; + } + else { + if (is_tap_win32_dev(enum_name)) { + snprintf(name, name_size, "%s", enum_name); + if (actual_name) { + if (strcmp(actual_name, "") != 0) { + if (strcmp(name_data, actual_name) != 0) { + RegCloseKey (connection_key); + ++i; + continue; + } + } + else { + snprintf(actual_name, actual_name_size, "%s", name_data); + } + } + stop = 1; + } + } + + RegCloseKey (connection_key); + } + ++i; + } + + RegCloseKey (control_net_key); + + if (stop == 0) + return -1; + + return 0; +} + +static int tap_win32_set_status(HANDLE handle, int status) +{ + unsigned long len = 0; + + return DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS, + &status, sizeof (status), + &status, sizeof (status), &len, NULL); +} + +static void tap_win32_overlapped_init(tap_win32_overlapped_t* const overlapped, const HANDLE handle) +{ + overlapped->handle = handle; + + overlapped->read_event = CreateEvent(NULL, FALSE, FALSE, NULL); + overlapped->write_event = CreateEvent(NULL, FALSE, FALSE, NULL); + + overlapped->read_overlapped.Offset = 0; + overlapped->read_overlapped.OffsetHigh = 0; + overlapped->read_overlapped.hEvent = overlapped->read_event; + + overlapped->write_overlapped.Offset = 0; + overlapped->write_overlapped.OffsetHigh = 0; + overlapped->write_overlapped.hEvent = overlapped->write_event; + + InitializeCriticalSection(&overlapped->output_queue_cs); + InitializeCriticalSection(&overlapped->free_list_cs); + + overlapped->output_queue_semaphore = CreateSemaphore( + NULL, // default security attributes + 0, // initial count + TUN_MAX_BUFFER_COUNT, // maximum count + NULL); // unnamed semaphore + + if(!overlapped->output_queue_semaphore) { + fprintf(stderr, "error creating output queue semaphore!\n"); + } + + overlapped->free_list_semaphore = CreateSemaphore( + NULL, // default security attributes + TUN_MAX_BUFFER_COUNT, // initial count + TUN_MAX_BUFFER_COUNT, // maximum count + NULL); // unnamed semaphore + + if(!overlapped->free_list_semaphore) { + fprintf(stderr, "error creating free list semaphore!\n"); + } + + overlapped->free_list = overlapped->output_queue_front = overlapped->output_queue_back = NULL; + + { + unsigned index; + for(index = 0; index < TUN_MAX_BUFFER_COUNT; index++) { + tun_buffer_t* element = &overlapped->buffers[index]; + element->next = overlapped->free_list; + overlapped->free_list = element; + } + } +} + +static int tap_win32_write(tap_win32_overlapped_t *overlapped, + const void *buffer, unsigned long size) +{ + unsigned long write_size; + BOOL result; + DWORD error; + + result = GetOverlappedResult( overlapped->handle, &overlapped->write_overlapped, + &write_size, FALSE); + + if (!result && GetLastError() == ERROR_IO_INCOMPLETE) + WaitForSingleObject(overlapped->write_event, INFINITE); + + result = WriteFile(overlapped->handle, buffer, size, + &write_size, &overlapped->write_overlapped); + + if (!result) { + switch (error = GetLastError()) + { + case ERROR_IO_PENDING: +#ifndef TUN_ASYNCHRONOUS_WRITES + WaitForSingleObject(overlapped->write_event, INFINITE); +#endif + break; + default: + return -1; + } + } + + return 0; +} + +static DWORD WINAPI tap_win32_thread_entry(LPVOID param) +{ + tap_win32_overlapped_t *overlapped = (tap_win32_overlapped_t*)param; + unsigned long read_size; + BOOL result; + DWORD dwError; + tun_buffer_t* buffer = get_buffer_from_free_list(overlapped); + + + for (;;) { + result = ReadFile(overlapped->handle, + buffer->buffer, + sizeof(buffer->buffer), + &read_size, + &overlapped->read_overlapped); + if (!result) { + dwError = GetLastError(); + if (dwError == ERROR_IO_PENDING) { + WaitForSingleObject(overlapped->read_event, INFINITE); + result = GetOverlappedResult( overlapped->handle, &overlapped->read_overlapped, + &read_size, FALSE); + if (!result) { +#if DEBUG_TAP_WIN32 + LPVOID lpBuffer; + dwError = GetLastError(); + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) & lpBuffer, 0, NULL ); + fprintf(stderr, "Tap-Win32: Error GetOverlappedResult %d - %s\n", dwError, lpBuffer); + LocalFree( lpBuffer ); +#endif + } + } else { +#if DEBUG_TAP_WIN32 + LPVOID lpBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) & lpBuffer, 0, NULL ); + fprintf(stderr, "Tap-Win32: Error ReadFile %d - %s\n", dwError, lpBuffer); + LocalFree( lpBuffer ); +#endif + } + } + + if(read_size > 0) { + buffer->read_size = read_size; + put_buffer_on_output_queue(overlapped, buffer); + buffer = get_buffer_from_free_list(overlapped); + } + } + + return 0; +} + +static int tap_win32_read(tap_win32_overlapped_t *overlapped, + uint8_t **pbuf, int max_size) +{ + int size = 0; + + tun_buffer_t* buffer = get_buffer_from_output_queue_immediate(overlapped); + + if(buffer != NULL) { + *pbuf = buffer->buffer; + size = (int)buffer->read_size; + if(size > max_size) { + size = max_size; + } + } + + return size; +} + +static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped, + char* pbuf) +{ + tun_buffer_t* buffer = (tun_buffer_t*)pbuf; + put_buffer_on_free_list(overlapped, buffer); +} + +static int tap_win32_open(tap_win32_overlapped_t **phandle, + const char *prefered_name) +{ + char device_path[256]; + char device_guid[0x100]; + int rc; + HANDLE handle; + BOOL bret; + char name_buffer[0x100] = {0, }; + struct { + unsigned long major; + unsigned long minor; + unsigned long debug; + } version; + LONG version_len; + DWORD idThread; + HANDLE hThread; + + if (prefered_name != NULL) + snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name); + + rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer)); + if (rc) + return -1; + + snprintf (device_path, sizeof(device_path), "%s%s%s", + USERMODEDEVICEDIR, + device_guid, + TAPSUFFIX); + + handle = CreateFile ( + device_path, + GENERIC_READ | GENERIC_WRITE, + 0, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + 0 ); + + if (handle == INVALID_HANDLE_VALUE) { + return -1; + } + + bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION, + &version, sizeof (version), + &version, sizeof (version), &version_len, NULL); + + if (bret == FALSE) { + CloseHandle(handle); + return -1; + } + + if (!tap_win32_set_status(handle, TRUE)) { + return -1; + } + + tap_win32_overlapped_init(&tap_overlapped, handle); + + *phandle = &tap_overlapped; + + hThread = CreateThread(NULL, 0, tap_win32_thread_entry, + (LPVOID)&tap_overlapped, 0, &idThread); + SetThreadPriority(hThread,THREAD_PRIORITY_TIME_CRITICAL); + + return 0; +} + +/********************************************/ + + typedef struct TAPState { + VLANClientState *vc; + tap_win32_overlapped_t *handle; + } TAPState; + +static TAPState *tap_win32_state = NULL; + +static void tap_receive(void *opaque, const uint8_t *buf, int size) +{ + TAPState *s = opaque; + + tap_win32_write(s->handle, buf, size); +} + +/* XXX: horrible, suppress this by using proper thread signaling */ +void tap_win32_poll(void) +{ + TAPState *s = tap_win32_state; + uint8_t *buf; + int max_size = 4096; + int size; + + if (!s) + return; + + size = tap_win32_read(s->handle, &buf, max_size); + if (size > 0) { + qemu_send_packet(s->vc, buf, size); + tap_win32_free_buffer(s->handle, buf); + } +} + +int tap_win32_init(VLANState *vlan, const char *ifname) +{ + TAPState *s; + + s = qemu_mallocz(sizeof(TAPState)); + if (!s) + return -1; + if (tap_win32_open(&s->handle, ifname) < 0) { + printf("tap: Could not open '%s'\n", ifname); + return -1; + } + + s->vc = qemu_new_vlan_client(vlan, tap_receive, NULL, s); + + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "tap: ifname=%s", ifname); + tap_win32_state = s; + return 0; +} diff --git a/tools/ioemu/target-i386-dm/cpu.h b/tools/ioemu/target-i386-dm/cpu.h new file mode 100644 index 0000000000..2a7d15019c --- /dev/null +++ b/tools/ioemu/target-i386-dm/cpu.h @@ -0,0 +1,86 @@ +/* + * i386 virtual CPU header + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef CPU_I386_H +#define CPU_I386_H + +#include "config.h" + +#ifdef TARGET_X86_64 +#define TARGET_LONG_BITS 64 +#else +#define TARGET_LONG_BITS 32 +#endif + +/* target supports implicit self modifying code */ +#define TARGET_HAS_SMC +/* support for self modifying code even if the modified instruction is + close to the modifying instruction */ +#define TARGET_HAS_PRECISE_SMC + +#include "cpu-defs.h" + +#include "softfloat.h" + +#if defined(__i386__) && !defined(CONFIG_SOFTMMU) +#define USE_CODE_COPY +#endif + +#ifdef USE_X86LDOUBLE +typedef floatx80 CPU86_LDouble; +#else +typedef float64 CPU86_LDouble; +#endif + +/* Empty for now */ +typedef struct CPUX86State { + uint32_t a20_mask; + + int interrupt_request; + + CPU_COMMON + + int send_event; +} CPUX86State; + +CPUX86State *cpu_x86_init(void); +int cpu_x86_exec(CPUX86State *s); +void cpu_x86_close(CPUX86State *s); +int cpu_get_pic_interrupt(CPUX86State *s); +/* MSDOS compatibility mode FPU exception support */ +void cpu_set_ferr(CPUX86State *s); + +void cpu_x86_set_a20(CPUX86State *env, int a20_state); + +#ifndef IN_OP_I386 +void cpu_x86_outb(CPUX86State *env, int addr, int val); +void cpu_x86_outw(CPUX86State *env, int addr, int val); +void cpu_x86_outl(CPUX86State *env, int addr, int val); +int cpu_x86_inb(CPUX86State *env, int addr); +int cpu_x86_inw(CPUX86State *env, int addr); +int cpu_x86_inl(CPUX86State *env, int addr); +#endif + +/* helper2.c */ +int main_loop(void); + +#define TARGET_PAGE_BITS 12 +#include "cpu-all.h" + +#endif /* CPU_I386_H */ diff --git a/tools/ioemu/target-i386-dm/exec-dm.c b/tools/ioemu/target-i386-dm/exec-dm.c new file mode 100644 index 0000000000..0724791f2a --- /dev/null +++ b/tools/ioemu/target-i386-dm/exec-dm.c @@ -0,0 +1,512 @@ +/* + * virtual page mapping and translated block handling + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "config.h" +#ifdef _WIN32 +#include +#else +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "exec-all.h" + +//#define DEBUG_TB_INVALIDATE +//#define DEBUG_FLUSH +//#define DEBUG_TLB + +/* make various TB consistency checks */ +//#define DEBUG_TB_CHECK +//#define DEBUG_TLB_CHECK + +#ifndef CONFIG_DM +/* threshold to flush the translated code buffer */ +#define CODE_GEN_BUFFER_MAX_SIZE (CODE_GEN_BUFFER_SIZE - CODE_GEN_MAX_SIZE) + +#define SMC_BITMAP_USE_THRESHOLD 10 + +#define MMAP_AREA_START 0x00000000 +#define MMAP_AREA_END 0xa8000000 + +TranslationBlock tbs[CODE_GEN_MAX_BLOCKS]; +TranslationBlock *tb_hash[CODE_GEN_HASH_SIZE]; +TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; +int nb_tbs; +/* any access to the tbs or the page table must use this lock */ +spinlock_t tb_lock = SPIN_LOCK_UNLOCKED; + +uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; +uint8_t *code_gen_ptr; +#endif /* !CONFIG_DM */ + +uint64_t phys_ram_size; +int phys_ram_fd; +uint8_t *phys_ram_base; +uint8_t *phys_ram_dirty; + +CPUState *first_cpu; +/* current CPU in the current thread. It is only valid inside + cpu_exec() */ +CPUState *cpu_single_env; + +typedef struct PageDesc { + /* list of TBs intersecting this ram page */ + TranslationBlock *first_tb; + /* in order to optimize self modifying code, we count the number + of lookups we do to a given page to use a bitmap */ + unsigned int code_write_count; + uint8_t *code_bitmap; +#if defined(CONFIG_USER_ONLY) + unsigned long flags; +#endif +} PageDesc; + +typedef struct PhysPageDesc { + /* offset in host memory of the page + io_index in the low 12 bits */ + unsigned long phys_offset; +} PhysPageDesc; + +typedef struct VirtPageDesc { + /* physical address of code page. It is valid only if 'valid_tag' + matches 'virt_valid_tag' */ + target_ulong phys_addr; + unsigned int valid_tag; +#if !defined(CONFIG_SOFTMMU) + /* original page access rights. It is valid only if 'valid_tag' + matches 'virt_valid_tag' */ + unsigned int prot; +#endif +} VirtPageDesc; + +#define L2_BITS 10 +#define L1_BITS (32 - L2_BITS - TARGET_PAGE_BITS) + +#define L1_SIZE (1 << L1_BITS) +#define L2_SIZE (1 << L2_BITS) + +unsigned long qemu_real_host_page_size; +unsigned long qemu_host_page_bits; +unsigned long qemu_host_page_size; +unsigned long qemu_host_page_mask; + +/* io memory support */ +CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; +CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4]; +void *io_mem_opaque[IO_MEM_NB_ENTRIES]; +static int io_mem_nb = 1; + +/* log support */ +char *logfilename = "/tmp/qemu.log"; +FILE *logfile; +int loglevel; + +void cpu_exec_init(CPUState *env) +{ + CPUState **penv; + int cpu_index; + + env->next_cpu = NULL; + penv = &first_cpu; + cpu_index = 0; + while (*penv != NULL) { + penv = (CPUState **)&(*penv)->next_cpu; + cpu_index++; + } + env->cpu_index = cpu_index; + *penv = env; + + /* alloc dirty bits array */ + phys_ram_dirty = qemu_malloc(phys_ram_size >> TARGET_PAGE_BITS); +} + +/* enable or disable low levels log */ +void cpu_set_log(int log_flags) +{ + loglevel = log_flags; + if (!logfile) { + logfile = fopen(logfilename, "w"); + if (!logfile) { + perror(logfilename); + _exit(1); + } +#if !defined(CONFIG_SOFTMMU) + /* must avoid mmap() usage of glibc by setting a buffer "by hand" */ + { + static uint8_t logfile_buf[4096]; + setvbuf(logfile, logfile_buf, _IOLBF, sizeof(logfile_buf)); + } +#else + setvbuf(logfile, NULL, _IOLBF, 0); +#endif + stdout = logfile; + stderr = logfile; + } +} + +void cpu_set_log_filename(const char *filename) +{ + logfilename = strdup(filename); +} + +/* mask must never be zero, except for A20 change call */ +void cpu_interrupt(CPUState *env, int mask) +{ + env->interrupt_request |= mask; +} + +void cpu_reset_interrupt(CPUState *env, int mask) +{ + env->interrupt_request &= ~mask; +} + +CPULogItem cpu_log_items[] = { + { CPU_LOG_TB_OUT_ASM, "out_asm", + "show generated host assembly code for each compiled TB" }, + { CPU_LOG_TB_IN_ASM, "in_asm", + "show target assembly code for each compiled TB" }, + { CPU_LOG_TB_OP, "op", + "show micro ops for each compiled TB (only usable if 'in_asm' used)" }, +#ifdef TARGET_I386 + { CPU_LOG_TB_OP_OPT, "op_opt", + "show micro ops after optimization for each compiled TB" }, +#endif + { CPU_LOG_INT, "int", + "show interrupts/exceptions in short format" }, + { CPU_LOG_EXEC, "exec", + "show trace before each executed TB (lots of logs)" }, + { CPU_LOG_TB_CPU, "cpu", + "show CPU state before bloc translation" }, +#ifdef TARGET_I386 + { CPU_LOG_PCALL, "pcall", + "show protected mode far calls/returns/exceptions" }, +#endif +#ifdef DEBUG_IOPORT + { CPU_LOG_IOPORT, "ioport", + "show all i/o ports accesses" }, +#endif + { 0, NULL, NULL }, +}; + +static int cmp1(const char *s1, int n, const char *s2) +{ + if (strlen(s2) != n) + return 0; + return memcmp(s1, s2, n) == 0; +} + +/* takes a comma separated list of log masks. Return 0 if error. */ +int cpu_str_to_log_mask(const char *str) +{ + CPULogItem *item; + int mask; + const char *p, *p1; + + p = str; + mask = 0; + for(;;) { + p1 = strchr(p, ','); + if (!p1) + p1 = p + strlen(p); + if(cmp1(p,p1-p,"all")) { + for(item = cpu_log_items; item->mask != 0; item++) { + mask |= item->mask; + } + } else { + for(item = cpu_log_items; item->mask != 0; item++) { + if (cmp1(p, p1 - p, item->name)) + goto found; + } + return 0; + } + found: + mask |= item->mask; + if (*p1 != ',') + break; + p = p1 + 1; + } + return mask; +} + +void cpu_abort(CPUState *env, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "qemu: fatal: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + abort(); +} + + +/* XXX: Simple implementation. Fix later */ +#define MAX_MMIO 32 +struct mmio_space { + target_phys_addr_t start; + unsigned long size; + unsigned long io_index; +} mmio[MAX_MMIO]; +unsigned long mmio_cnt; + +/* register physical memory. 'size' must be a multiple of the target + page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an + io memory page */ +void cpu_register_physical_memory(target_phys_addr_t start_addr, + unsigned long size, + unsigned long phys_offset) +{ + int i; + + for (i = 0; i < mmio_cnt; i++) { + if(mmio[i].start == start_addr) { + mmio[i].io_index = phys_offset; + mmio[i].size = size; + return; + } + } + + if (mmio_cnt == MAX_MMIO) { + fprintf(logfile, "too many mmio regions\n"); + exit(-1); + } + + mmio[mmio_cnt].io_index = phys_offset; + mmio[mmio_cnt].start = start_addr; + mmio[mmio_cnt++].size = size; +} + +/* mem_read and mem_write are arrays of functions containing the + function to access byte (index 0), word (index 1) and dword (index + 2). All functions must be supplied. If io_index is non zero, the + corresponding io zone is modified. If it is zero, a new io zone is + allocated. The return value can be used with + cpu_register_physical_memory(). (-1) is returned if error. */ +int cpu_register_io_memory(int io_index, + CPUReadMemoryFunc **mem_read, + CPUWriteMemoryFunc **mem_write, + void *opaque) +{ + int i; + + if (io_index <= 0) { + if (io_index >= IO_MEM_NB_ENTRIES) + return -1; + io_index = io_mem_nb++; + } else { + if (io_index >= IO_MEM_NB_ENTRIES) + return -1; + } + + for(i = 0;i < 3; i++) { + io_mem_read[io_index][i] = mem_read[i]; + io_mem_write[io_index][i] = mem_write[i]; + } + io_mem_opaque[io_index] = opaque; + return io_index << IO_MEM_SHIFT; +} + +CPUWriteMemoryFunc **cpu_get_io_memory_write(int io_index) +{ + return io_mem_write[io_index >> IO_MEM_SHIFT]; +} + +CPUReadMemoryFunc **cpu_get_io_memory_read(int io_index) +{ + return io_mem_read[io_index >> IO_MEM_SHIFT]; +} + +/* physical memory access (slow version, mainly for debug) */ +#if defined(CONFIG_USER_ONLY) +void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, + int len, int is_write) +{ + int l, flags; + target_ulong page; + + while (len > 0) { + page = addr & TARGET_PAGE_MASK; + l = (page + TARGET_PAGE_SIZE) - addr; + if (l > len) + l = len; + flags = page_get_flags(page); + if (!(flags & PAGE_VALID)) + return; + if (is_write) { + if (!(flags & PAGE_WRITE)) + return; + memcpy((uint8_t *)addr, buf, len); + } else { + if (!(flags & PAGE_READ)) + return; + memcpy(buf, (uint8_t *)addr, len); + } + len -= l; + buf += l; + addr += l; + } +} +#else + +int iomem_index(target_phys_addr_t addr) +{ + int i; + + for (i = 0; i < mmio_cnt; i++) { + unsigned long start, end; + + start = mmio[i].start; + end = mmio[i].start + mmio[i].size; + + if ((addr >= start) && (addr <= end)){ + return (mmio[i].io_index >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + } + } + return 0; +} + +void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, + int len, int is_write) +{ + int l, io_index; + uint8_t *ptr; + uint32_t val; + target_phys_addr_t page; + unsigned long pd; + + while (len > 0) { + page = addr & TARGET_PAGE_MASK; + l = (page + TARGET_PAGE_SIZE) - addr; + if (l > len) + l = len; + + pd = page; + io_index = iomem_index(page); + if (is_write) { + if (io_index) { + if (l >= 4 && ((addr & 3) == 0)) { + /* 32 bit read access */ + val = ldl_raw(buf); + io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val); + l = 4; + } else if (l >= 2 && ((addr & 1) == 0)) { + /* 16 bit read access */ + val = lduw_raw(buf); + io_mem_write[io_index][1](io_mem_opaque[io_index], addr, val); + l = 2; + } else { + /* 8 bit access */ + val = ldub_raw(buf); + io_mem_write[io_index][0](io_mem_opaque[io_index], addr, val); + l = 1; + } + } else { + unsigned long addr1; + + addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); + /* RAM case */ + ptr = phys_ram_base + addr1; + memcpy(ptr, buf, l); + } + } else { + if (io_index) { + if (l >= 4 && ((addr & 3) == 0)) { + /* 32 bit read access */ + val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr); + stl_raw(buf, val); + l = 4; + } else if (l >= 2 && ((addr & 1) == 0)) { + /* 16 bit read access */ + val = io_mem_read[io_index][1](io_mem_opaque[io_index], addr); + stw_raw(buf, val); + l = 2; + } else { + /* 8 bit access */ + val = io_mem_read[io_index][0](io_mem_opaque[io_index], addr); + stb_raw(buf, val); + l = 1; + } + } else { + /* RAM case */ + ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + + (addr & ~TARGET_PAGE_MASK); + memcpy(buf, ptr, l); + } + } + len -= l; + buf += l; + addr += l; + } +} +#endif + +/* virtual memory access for debug */ +int cpu_memory_rw_debug(CPUState *env, target_ulong addr, + uint8_t *buf, int len, int is_write) +{ + int l; + target_ulong page, phys_addr; + + while (len > 0) { + page = addr & TARGET_PAGE_MASK; + phys_addr = cpu_get_phys_page_debug(env, page); + /* if no physical page mapped, return an error */ + if (phys_addr == -1) + return -1; + l = (page + TARGET_PAGE_SIZE) - addr; + if (l > len) + l = len; + cpu_physical_memory_rw(phys_addr + (addr & ~TARGET_PAGE_MASK), + buf, l, is_write); + len -= l; + buf += l; + addr += l; + } + return 0; +} + +void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, + int dirty_flags) +{ + unsigned long length; + int i, mask, len; + uint8_t *p; + + start &= TARGET_PAGE_MASK; + end = TARGET_PAGE_ALIGN(end); + + length = end - start; + if (length == 0) + return; + mask = ~dirty_flags; + p = phys_ram_dirty + (start >> TARGET_PAGE_BITS); + len = length >> TARGET_PAGE_BITS; + for(i = 0; i < len; i++) + p[i] &= mask; + + return; +} diff --git a/tools/ioemu/target-i386-dm/helper2.c b/tools/ioemu/target-i386-dm/helper2.c new file mode 100644 index 0000000000..ffa24ac190 --- /dev/null +++ b/tools/ioemu/target-i386-dm/helper2.c @@ -0,0 +1,507 @@ +/* + * i386 helpers (without register variable usage) + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Main cpu loop for handling I/O requests coming from a virtual machine + * Copyright © 2004, Intel Corporation. + * Copyright © 2005, International Business Machines Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "cpu.h" +#include "exec-all.h" + +//#define DEBUG_MMU + +#ifdef USE_CODE_COPY +#include +#include +#include + +_syscall3(int, modify_ldt, int, func, void *, ptr, unsigned long, bytecount) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 66) +#define modify_ldt_ldt_s user_desc +#endif +#endif /* USE_CODE_COPY */ + +#include "vl.h" + +int domid = -1; +int vcpus = 1; + +int xc_handle; + +shared_iopage_t *shared_page = NULL; + +/* the evtchn fd for polling */ +int xce_handle = -1; + +/* which vcpu we are serving */ +int send_vcpu = 0; + +CPUX86State *cpu_x86_init(void) +{ + CPUX86State *env; + static int inited; + int i, rc; + + env = qemu_mallocz(sizeof(CPUX86State)); + if (!env) + return NULL; + cpu_exec_init(env); + + /* init various static tables */ + if (!inited) { + inited = 1; + + cpu_single_env = env; + + xce_handle = xc_evtchn_open(); + if (xce_handle == -1) { + perror("open"); + return NULL; + } + + /* FIXME: how about if we overflow the page here? */ + for (i = 0; i < vcpus; i++) { + rc = xc_evtchn_bind_interdomain( + xce_handle, domid, shared_page->vcpu_iodata[i].vp_eport); + if (rc == -1) { + fprintf(logfile, "bind interdomain ioctl error %d\n", errno); + return NULL; + } + shared_page->vcpu_iodata[i].dm_eport = rc; + } + } + + return env; +} + +/* called from main_cpu_reset */ +void cpu_reset(CPUX86State *env) +{ + int xcHandle; + int sts; + + /* pause domain first, to avoid repeated reboot request*/ + xc_domain_pause(xc_handle, domid); + + xcHandle = xc_interface_open(); + if (xcHandle < 0) + fprintf(logfile, "Cannot acquire xenctrl handle\n"); + else { + sts = xc_domain_shutdown(xcHandle, domid, SHUTDOWN_reboot); + if (sts != 0) + fprintf(logfile, + "? xc_domain_shutdown failed to issue reboot, sts %d\n", + sts); + else + fprintf(logfile, "Issued domain %d reboot\n", domid); + xc_interface_close(xcHandle); + } +} + +void cpu_x86_close(CPUX86State *env) +{ + free(env); +} + + +void cpu_dump_state(CPUState *env, FILE *f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...), + int flags) +{ +} + +/***********************************************************/ +/* x86 mmu */ +/* XXX: add PGE support */ + +void cpu_x86_set_a20(CPUX86State *env, int a20_state) +{ + a20_state = (a20_state != 0); + if (a20_state != ((env->a20_mask >> 20) & 1)) { +#if defined(DEBUG_MMU) + printf("A20 update: a20=%d\n", a20_state); +#endif + env->a20_mask = 0xffefffff | (a20_state << 20); + } +} + +target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + return addr; +} + +//some functions to handle the io req packet +void sp_info() +{ + ioreq_t *req; + int i; + + for (i = 0; i < vcpus; i++) { + req = &(shared_page->vcpu_iodata[i].vp_ioreq); + term_printf("vcpu %d: event port %d\n", i, + shared_page->vcpu_iodata[i].vp_eport); + term_printf(" req state: %x, pvalid: %x, addr: %"PRIx64", " + "data: %"PRIx64", count: %"PRIx64", size: %"PRIx64"\n", + req->state, req->pdata_valid, req->addr, + req->u.data, req->count, req->size); + term_printf(" IO totally occurred on this vcpu: %"PRIx64"\n", + req->io_count); + } +} + +//get the ioreq packets from share mem +static ioreq_t *__cpu_get_ioreq(int vcpu) +{ + ioreq_t *req; + + req = &(shared_page->vcpu_iodata[vcpu].vp_ioreq); + + if (req->state == STATE_IOREQ_READY) { + req->state = STATE_IOREQ_INPROCESS; + return req; + } + + fprintf(logfile, "False I/O request ... in-service already: " + "%x, pvalid: %x, port: %"PRIx64", " + "data: %"PRIx64", count: %"PRIx64", size: %"PRIx64"\n", + req->state, req->pdata_valid, req->addr, + req->u.data, req->count, req->size); + return NULL; +} + +//use poll to get the port notification +//ioreq_vec--out,the +//retval--the number of ioreq packet +static ioreq_t *cpu_get_ioreq(void) +{ + int i; + evtchn_port_t port; + + port = xc_evtchn_pending(xce_handle); + if (port != -1) { + for ( i = 0; i < vcpus; i++ ) + if ( shared_page->vcpu_iodata[i].dm_eport == port ) + break; + + if ( i == vcpus ) { + fprintf(logfile, "Fatal error while trying to get io event!\n"); + exit(1); + } + + // unmask the wanted port again + xc_evtchn_unmask(xce_handle, port); + + //get the io packet from shared memory + send_vcpu = i; + return __cpu_get_ioreq(i); + } + + //read error or read nothing + return NULL; +} + +unsigned long do_inp(CPUState *env, unsigned long addr, unsigned long size) +{ + switch(size) { + case 1: + return cpu_inb(env, addr); + case 2: + return cpu_inw(env, addr); + case 4: + return cpu_inl(env, addr); + default: + fprintf(logfile, "inp: bad size: %lx %lx\n", addr, size); + exit(-1); + } +} + +void do_outp(CPUState *env, unsigned long addr, + unsigned long size, unsigned long val) +{ + switch(size) { + case 1: + return cpu_outb(env, addr, val); + case 2: + return cpu_outw(env, addr, val); + case 4: + return cpu_outl(env, addr, val); + default: + fprintf(logfile, "outp: bad size: %lx %lx\n", addr, size); + exit(-1); + } +} + +extern void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, + int len, int is_write); + +static inline void read_physical(uint64_t addr, unsigned long size, void *val) +{ + return cpu_physical_memory_rw((target_phys_addr_t)addr, val, size, 0); +} + +static inline void write_physical(uint64_t addr, unsigned long size, void *val) +{ + return cpu_physical_memory_rw((target_phys_addr_t)addr, val, size, 1); +} + +void cpu_ioreq_pio(CPUState *env, ioreq_t *req) +{ + int i, sign; + + sign = req->df ? -1 : 1; + + if (req->dir == IOREQ_READ) { + if (!req->pdata_valid) { + req->u.data = do_inp(env, req->addr, req->size); + } else { + unsigned long tmp; + + for (i = 0; i < req->count; i++) { + tmp = do_inp(env, req->addr, req->size); + write_physical((target_phys_addr_t) req->u.pdata + + (sign * i * req->size), + req->size, &tmp); + } + } + } else if (req->dir == IOREQ_WRITE) { + if (!req->pdata_valid) { + do_outp(env, req->addr, req->size, req->u.data); + } else { + for (i = 0; i < req->count; i++) { + unsigned long tmp; + + read_physical((target_phys_addr_t) req->u.pdata + + (sign * i * req->size), + req->size, &tmp); + do_outp(env, req->addr, req->size, tmp); + } + } + } +} + +void cpu_ioreq_move(CPUState *env, ioreq_t *req) +{ + int i, sign; + + sign = req->df ? -1 : 1; + + if (!req->pdata_valid) { + if (req->dir == IOREQ_READ) { + for (i = 0; i < req->count; i++) { + read_physical(req->addr + + (sign * i * req->size), + req->size, &req->u.data); + } + } else if (req->dir == IOREQ_WRITE) { + for (i = 0; i < req->count; i++) { + write_physical(req->addr + + (sign * i * req->size), + req->size, &req->u.data); + } + } + } else { + unsigned long tmp; + + if (req->dir == IOREQ_READ) { + for (i = 0; i < req->count; i++) { + read_physical(req->addr + + (sign * i * req->size), + req->size, &tmp); + write_physical((target_phys_addr_t )req->u.pdata + + (sign * i * req->size), + req->size, &tmp); + } + } else if (req->dir == IOREQ_WRITE) { + for (i = 0; i < req->count; i++) { + read_physical((target_phys_addr_t) req->u.pdata + + (sign * i * req->size), + req->size, &tmp); + write_physical(req->addr + + (sign * i * req->size), + req->size, &tmp); + } + } + } +} + +void cpu_ioreq_and(CPUState *env, ioreq_t *req) +{ + unsigned long tmp1, tmp2; + + if (req->pdata_valid != 0) + hw_error("expected scalar value"); + + read_physical(req->addr, req->size, &tmp1); + if (req->dir == IOREQ_WRITE) { + tmp2 = tmp1 & (unsigned long) req->u.data; + write_physical(req->addr, req->size, &tmp2); + } + req->u.data = tmp1; +} + +void cpu_ioreq_or(CPUState *env, ioreq_t *req) +{ + unsigned long tmp1, tmp2; + + if (req->pdata_valid != 0) + hw_error("expected scalar value"); + + read_physical(req->addr, req->size, &tmp1); + if (req->dir == IOREQ_WRITE) { + tmp2 = tmp1 | (unsigned long) req->u.data; + write_physical(req->addr, req->size, &tmp2); + } + req->u.data = tmp1; +} + +void cpu_ioreq_xor(CPUState *env, ioreq_t *req) +{ + unsigned long tmp1, tmp2; + + if (req->pdata_valid != 0) + hw_error("expected scalar value"); + + read_physical(req->addr, req->size, &tmp1); + if (req->dir == IOREQ_WRITE) { + tmp2 = tmp1 ^ (unsigned long) req->u.data; + write_physical(req->addr, req->size, &tmp2); + } + req->u.data = tmp1; +} + +void cpu_handle_ioreq(void *opaque) +{ + CPUState *env = opaque; + ioreq_t *req = cpu_get_ioreq(); + + if (req) { + if ((!req->pdata_valid) && (req->dir == IOREQ_WRITE)) { + if (req->size != 4) + req->u.data &= (1UL << (8 * req->size))-1; + } + + switch (req->type) { + case IOREQ_TYPE_PIO: + cpu_ioreq_pio(env, req); + break; + case IOREQ_TYPE_COPY: + cpu_ioreq_move(env, req); + break; + case IOREQ_TYPE_AND: + cpu_ioreq_and(env, req); + break; + case IOREQ_TYPE_OR: + cpu_ioreq_or(env, req); + break; + case IOREQ_TYPE_XOR: + cpu_ioreq_xor(env, req); + break; + default: + hw_error("Invalid ioreq type 0x%x\n", req->type); + } + + /* No state change if state = STATE_IORESP_HOOK */ + if (req->state == STATE_IOREQ_INPROCESS) + req->state = STATE_IORESP_READY; + env->send_event = 1; + } +} + +int main_loop(void) +{ + extern int vm_running; + extern int shutdown_requested; + CPUState *env = cpu_single_env; + int evtchn_fd = xc_evtchn_fd(xce_handle); + + qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, env); + + env->send_event = 0; + + while (1) { + if (vm_running) { + if (shutdown_requested) + break; + if (reset_requested) { + qemu_system_reset(); + reset_requested = 0; + } + } + + /* Wait up to 10 msec. */ + main_loop_wait(10); + + if (env->send_event) { + env->send_event = 0; + xc_evtchn_notify(xce_handle, + shared_page->vcpu_iodata[send_vcpu].dm_eport); + } + } + destroy_hvm_domain(); + return 0; +} + +void destroy_hvm_domain(void) +{ + int xcHandle; + int sts; + + xcHandle = xc_interface_open(); + if (xcHandle < 0) + fprintf(logfile, "Cannot acquire xenctrl handle\n"); + else { + sts = xc_domain_shutdown(xcHandle, domid, SHUTDOWN_poweroff); + if (sts != 0) + fprintf(logfile, "? xc_domain_shutdown failed to issue poweroff, " + "sts %d, errno %d\n", sts, errno); + else + fprintf(logfile, "Issued domain %d poweroff\n", domid); + xc_interface_close(xcHandle); + } +} diff --git a/tools/ioemu/target-i386-dm/i8259-dm.c b/tools/ioemu/target-i386-dm/i8259-dm.c new file mode 100644 index 0000000000..3243b63b5d --- /dev/null +++ b/tools/ioemu/target-i386-dm/i8259-dm.c @@ -0,0 +1,107 @@ +/* Xen 8259 stub for interrupt controller emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2005 Intel corperation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* debug PIC */ +//#define DEBUG_PIC + +//#define DEBUG_IRQ_LATENCY +//#define DEBUG_IRQ_COUNT + +#include "xenctrl.h" +#include +#include +#include "cpu.h" +#include "cpu-all.h" + +extern shared_iopage_t *shared_page; + +struct PicState2 { +}; + +void pic_set_irq_new(void *opaque, int irq, int level) +{ + /* PicState2 *s = opaque; */ + global_iodata_t *gio; + int mask; + + gio = &shared_page->sp_global; + mask = 1 << irq; + if ( gio->pic_elcr & mask ) { + /* level */ + if ( level ) { + atomic_clear_bit(irq, &gio->pic_clear_irr); + atomic_set_bit(irq, &gio->pic_irr); + cpu_single_env->send_event = 1; + } + else { + atomic_clear_bit(irq, &gio->pic_irr); + atomic_set_bit(irq, &gio->pic_clear_irr); + cpu_single_env->send_event = 1; + } + } + else { + /* edge */ + if ( level ) { + if ( (mask & gio->pic_last_irr) == 0 ) { + atomic_set_bit(irq, &gio->pic_irr); + atomic_set_bit(irq, &gio->pic_last_irr); + cpu_single_env->send_event = 1; + } + } + else { + atomic_clear_bit(irq, &gio->pic_last_irr); + } + } +} + +/* obsolete function */ +void pic_set_irq(int irq, int level) +{ + pic_set_irq_new(isa_pic, irq, level); +} + +void irq_info(void) +{ + term_printf("irq statistic code not compiled.\n"); +} + +void pic_info(void) +{ + term_printf("pic_info code not compiled.\n"); +} + +PicState2 *pic_init(IRQRequestFunc *irq_request, void *irq_request_opaque) +{ + PicState2 *s; + s = qemu_mallocz(sizeof(PicState2)); + if (!s) + return NULL; + return s; +} + +void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func, + void *alt_irq_opaque) +{ +} diff --git a/tools/ioemu/target-i386-dm/qemu-dm.debug b/tools/ioemu/target-i386-dm/qemu-dm.debug new file mode 100644 index 0000000000..3bad6d900d --- /dev/null +++ b/tools/ioemu/target-i386-dm/qemu-dm.debug @@ -0,0 +1,5 @@ +#!/bin/sh + +echo $* > /tmp/args +echo $DISPLAY >> /tmp/args +exec /usr/lib/xen/bin/qemu-dm $* diff --git a/tools/ioemu/target-i386-dm/qemu-ifup b/tools/ioemu/target-i386-dm/qemu-ifup new file mode 100644 index 0000000000..f852b86bf8 --- /dev/null +++ b/tools/ioemu/target-i386-dm/qemu-ifup @@ -0,0 +1,10 @@ +#!/bin/sh + +#. /etc/rc.d/init.d/functions +#ulimit -c unlimited + +echo -c 'config qemu network with xen bridge for ' +echo $* + +ifconfig $1 0.0.0.0 up +brctl addif $2 $1 diff --git a/tools/ioemu/target-i386/CVS/Entries b/tools/ioemu/target-i386/CVS/Entries new file mode 100644 index 0000000000..c230f3222b --- /dev/null +++ b/tools/ioemu/target-i386/CVS/Entries @@ -0,0 +1,13 @@ +/cpu.h/1.36/Thu May 25 18:22:39 2006//Trelease_0_8_1 +/exec.h/1.29/Thu May 25 18:22:39 2006//Trelease_0_8_1 +/helper.c/1.65/Thu May 25 18:22:39 2006//Trelease_0_8_1 +/helper2.c/1.41/Thu May 25 18:22:39 2006//Trelease_0_8_1 +/op.c/1.44/Thu May 25 18:22:39 2006//Trelease_0_8_1 +/opreg_template.h/1.3/Wed May 24 10:40:25 2006//Trelease_0_8_1 +/ops_mem.h/1.7/Thu May 25 18:22:39 2006//Trelease_0_8_1 +/ops_sse.h/1.6/Tue Apr 26 20:38:17 2005//Trelease_0_8_1 +/ops_template.h/1.10/Wed May 24 10:40:25 2006//Trelease_0_8_1 +/ops_template_mem.h/1.6/Thu May 25 18:22:39 2006//Trelease_0_8_1 +/translate-copy.c/1.7/Wed May 24 10:40:25 2006//Trelease_0_8_1 +/translate.c/1.56/Thu May 25 18:22:39 2006//Trelease_0_8_1 +D diff --git a/tools/ioemu/target-i386/CVS/Repository b/tools/ioemu/target-i386/CVS/Repository new file mode 100644 index 0000000000..8e42d3620f --- /dev/null +++ b/tools/ioemu/target-i386/CVS/Repository @@ -0,0 +1 @@ +qemu/target-i386 diff --git a/tools/ioemu/target-i386/CVS/Root b/tools/ioemu/target-i386/CVS/Root new file mode 100644 index 0000000000..e8a9815e6a --- /dev/null +++ b/tools/ioemu/target-i386/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.savannah.nongnu.org:/sources/qemu diff --git a/tools/ioemu/target-i386/CVS/Tag b/tools/ioemu/target-i386/CVS/Tag new file mode 100644 index 0000000000..31c251a4bf --- /dev/null +++ b/tools/ioemu/target-i386/CVS/Tag @@ -0,0 +1 @@ +Nrelease_0_8_1 diff --git a/tools/ioemu/target-i386/cpu.h b/tools/ioemu/target-i386/cpu.h new file mode 100644 index 0000000000..2f23617303 --- /dev/null +++ b/tools/ioemu/target-i386/cpu.h @@ -0,0 +1,653 @@ +/* + * i386 virtual CPU header + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef CPU_I386_H +#define CPU_I386_H + +#include "config.h" + +#ifdef TARGET_X86_64 +#define TARGET_LONG_BITS 64 +#else +#define TARGET_LONG_BITS 32 +#endif + +/* target supports implicit self modifying code */ +#define TARGET_HAS_SMC +/* support for self modifying code even if the modified instruction is + close to the modifying instruction */ +#define TARGET_HAS_PRECISE_SMC + +#define TARGET_HAS_ICE 1 + +#include "cpu-defs.h" + +#include "softfloat.h" + +#if defined(__i386__) && !defined(CONFIG_SOFTMMU) +#define USE_CODE_COPY +#endif + +#define R_EAX 0 +#define R_ECX 1 +#define R_EDX 2 +#define R_EBX 3 +#define R_ESP 4 +#define R_EBP 5 +#define R_ESI 6 +#define R_EDI 7 + +#define R_AL 0 +#define R_CL 1 +#define R_DL 2 +#define R_BL 3 +#define R_AH 4 +#define R_CH 5 +#define R_DH 6 +#define R_BH 7 + +#define R_ES 0 +#define R_CS 1 +#define R_SS 2 +#define R_DS 3 +#define R_FS 4 +#define R_GS 5 + +/* segment descriptor fields */ +#define DESC_G_MASK (1 << 23) +#define DESC_B_SHIFT 22 +#define DESC_B_MASK (1 << DESC_B_SHIFT) +#define DESC_L_SHIFT 21 /* x86_64 only : 64 bit code segment */ +#define DESC_L_MASK (1 << DESC_L_SHIFT) +#define DESC_AVL_MASK (1 << 20) +#define DESC_P_MASK (1 << 15) +#define DESC_DPL_SHIFT 13 +#define DESC_S_MASK (1 << 12) +#define DESC_TYPE_SHIFT 8 +#define DESC_A_MASK (1 << 8) + +#define DESC_CS_MASK (1 << 11) /* 1=code segment 0=data segment */ +#define DESC_C_MASK (1 << 10) /* code: conforming */ +#define DESC_R_MASK (1 << 9) /* code: readable */ + +#define DESC_E_MASK (1 << 10) /* data: expansion direction */ +#define DESC_W_MASK (1 << 9) /* data: writable */ + +#define DESC_TSS_BUSY_MASK (1 << 9) + +/* eflags masks */ +#define CC_C 0x0001 +#define CC_P 0x0004 +#define CC_A 0x0010 +#define CC_Z 0x0040 +#define CC_S 0x0080 +#define CC_O 0x0800 + +#define TF_SHIFT 8 +#define IOPL_SHIFT 12 +#define VM_SHIFT 17 + +#define TF_MASK 0x00000100 +#define IF_MASK 0x00000200 +#define DF_MASK 0x00000400 +#define IOPL_MASK 0x00003000 +#define NT_MASK 0x00004000 +#define RF_MASK 0x00010000 +#define VM_MASK 0x00020000 +#define AC_MASK 0x00040000 +#define VIF_MASK 0x00080000 +#define VIP_MASK 0x00100000 +#define ID_MASK 0x00200000 + +/* hidden flags - used internally by qemu to represent additionnal cpu + states. Only the CPL, INHIBIT_IRQ and HALTED are not redundant. We avoid + using the IOPL_MASK, TF_MASK and VM_MASK bit position to ease oring + with eflags. */ +/* current cpl */ +#define HF_CPL_SHIFT 0 +/* true if soft mmu is being used */ +#define HF_SOFTMMU_SHIFT 2 +/* true if hardware interrupts must be disabled for next instruction */ +#define HF_INHIBIT_IRQ_SHIFT 3 +/* 16 or 32 segments */ +#define HF_CS32_SHIFT 4 +#define HF_SS32_SHIFT 5 +/* zero base for DS, ES and SS : can be '0' only in 32 bit CS segment */ +#define HF_ADDSEG_SHIFT 6 +/* copy of CR0.PE (protected mode) */ +#define HF_PE_SHIFT 7 +#define HF_TF_SHIFT 8 /* must be same as eflags */ +#define HF_MP_SHIFT 9 /* the order must be MP, EM, TS */ +#define HF_EM_SHIFT 10 +#define HF_TS_SHIFT 11 +#define HF_IOPL_SHIFT 12 /* must be same as eflags */ +#define HF_LMA_SHIFT 14 /* only used on x86_64: long mode active */ +#define HF_CS64_SHIFT 15 /* only used on x86_64: 64 bit code segment */ +#define HF_OSFXSR_SHIFT 16 /* CR4.OSFXSR */ +#define HF_VM_SHIFT 17 /* must be same as eflags */ +#define HF_HALTED_SHIFT 18 /* CPU halted */ + +#define HF_CPL_MASK (3 << HF_CPL_SHIFT) +#define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT) +#define HF_INHIBIT_IRQ_MASK (1 << HF_INHIBIT_IRQ_SHIFT) +#define HF_CS32_MASK (1 << HF_CS32_SHIFT) +#define HF_SS32_MASK (1 << HF_SS32_SHIFT) +#define HF_ADDSEG_MASK (1 << HF_ADDSEG_SHIFT) +#define HF_PE_MASK (1 << HF_PE_SHIFT) +#define HF_TF_MASK (1 << HF_TF_SHIFT) +#define HF_MP_MASK (1 << HF_MP_SHIFT) +#define HF_EM_MASK (1 << HF_EM_SHIFT) +#define HF_TS_MASK (1 << HF_TS_SHIFT) +#define HF_LMA_MASK (1 << HF_LMA_SHIFT) +#define HF_CS64_MASK (1 << HF_CS64_SHIFT) +#define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT) +#define HF_HALTED_MASK (1 << HF_HALTED_SHIFT) + +#define CR0_PE_MASK (1 << 0) +#define CR0_MP_MASK (1 << 1) +#define CR0_EM_MASK (1 << 2) +#define CR0_TS_MASK (1 << 3) +#define CR0_ET_MASK (1 << 4) +#define CR0_NE_MASK (1 << 5) +#define CR0_WP_MASK (1 << 16) +#define CR0_AM_MASK (1 << 18) +#define CR0_PG_MASK (1 << 31) + +#define CR4_VME_MASK (1 << 0) +#define CR4_PVI_MASK (1 << 1) +#define CR4_TSD_MASK (1 << 2) +#define CR4_DE_MASK (1 << 3) +#define CR4_PSE_MASK (1 << 4) +#define CR4_PAE_MASK (1 << 5) +#define CR4_PGE_MASK (1 << 7) +#define CR4_PCE_MASK (1 << 8) +#define CR4_OSFXSR_MASK (1 << 9) +#define CR4_OSXMMEXCPT_MASK (1 << 10) + +#define PG_PRESENT_BIT 0 +#define PG_RW_BIT 1 +#define PG_USER_BIT 2 +#define PG_PWT_BIT 3 +#define PG_PCD_BIT 4 +#define PG_ACCESSED_BIT 5 +#define PG_DIRTY_BIT 6 +#define PG_PSE_BIT 7 +#define PG_GLOBAL_BIT 8 +#define PG_NX_BIT 63 + +#define PG_PRESENT_MASK (1 << PG_PRESENT_BIT) +#define PG_RW_MASK (1 << PG_RW_BIT) +#define PG_USER_MASK (1 << PG_USER_BIT) +#define PG_PWT_MASK (1 << PG_PWT_BIT) +#define PG_PCD_MASK (1 << PG_PCD_BIT) +#define PG_ACCESSED_MASK (1 << PG_ACCESSED_BIT) +#define PG_DIRTY_MASK (1 << PG_DIRTY_BIT) +#define PG_PSE_MASK (1 << PG_PSE_BIT) +#define PG_GLOBAL_MASK (1 << PG_GLOBAL_BIT) +#define PG_NX_MASK (1LL << PG_NX_BIT) + +#define PG_ERROR_W_BIT 1 + +#define PG_ERROR_P_MASK 0x01 +#define PG_ERROR_W_MASK (1 << PG_ERROR_W_BIT) +#define PG_ERROR_U_MASK 0x04 +#define PG_ERROR_RSVD_MASK 0x08 +#define PG_ERROR_I_D_MASK 0x10 + +#define MSR_IA32_APICBASE 0x1b +#define MSR_IA32_APICBASE_BSP (1<<8) +#define MSR_IA32_APICBASE_ENABLE (1<<11) +#define MSR_IA32_APICBASE_BASE (0xfffff<<12) + +#define MSR_IA32_SYSENTER_CS 0x174 +#define MSR_IA32_SYSENTER_ESP 0x175 +#define MSR_IA32_SYSENTER_EIP 0x176 + +#define MSR_MCG_CAP 0x179 +#define MSR_MCG_STATUS 0x17a +#define MSR_MCG_CTL 0x17b + +#define MSR_PAT 0x277 + +#define MSR_EFER 0xc0000080 + +#define MSR_EFER_SCE (1 << 0) +#define MSR_EFER_LME (1 << 8) +#define MSR_EFER_LMA (1 << 10) +#define MSR_EFER_NXE (1 << 11) +#define MSR_EFER_FFXSR (1 << 14) + +#define MSR_STAR 0xc0000081 +#define MSR_LSTAR 0xc0000082 +#define MSR_CSTAR 0xc0000083 +#define MSR_FMASK 0xc0000084 +#define MSR_FSBASE 0xc0000100 +#define MSR_GSBASE 0xc0000101 +#define MSR_KERNELGSBASE 0xc0000102 + +/* cpuid_features bits */ +#define CPUID_FP87 (1 << 0) +#define CPUID_VME (1 << 1) +#define CPUID_DE (1 << 2) +#define CPUID_PSE (1 << 3) +#define CPUID_TSC (1 << 4) +#define CPUID_MSR (1 << 5) +#define CPUID_PAE (1 << 6) +#define CPUID_MCE (1 << 7) +#define CPUID_CX8 (1 << 8) +#define CPUID_APIC (1 << 9) +#define CPUID_SEP (1 << 11) /* sysenter/sysexit */ +#define CPUID_MTRR (1 << 12) +#define CPUID_PGE (1 << 13) +#define CPUID_MCA (1 << 14) +#define CPUID_CMOV (1 << 15) +#define CPUID_PAT (1 << 16) +#define CPUID_CLFLUSH (1 << 19) +/* ... */ +#define CPUID_MMX (1 << 23) +#define CPUID_FXSR (1 << 24) +#define CPUID_SSE (1 << 25) +#define CPUID_SSE2 (1 << 26) + +#define CPUID_EXT_SSE3 (1 << 0) +#define CPUID_EXT_MONITOR (1 << 3) +#define CPUID_EXT_CX16 (1 << 13) + +#define CPUID_EXT2_SYSCALL (1 << 11) +#define CPUID_EXT2_NX (1 << 20) +#define CPUID_EXT2_FFXSR (1 << 25) +#define CPUID_EXT2_LM (1 << 29) + +#define EXCP00_DIVZ 0 +#define EXCP01_SSTP 1 +#define EXCP02_NMI 2 +#define EXCP03_INT3 3 +#define EXCP04_INTO 4 +#define EXCP05_BOUND 5 +#define EXCP06_ILLOP 6 +#define EXCP07_PREX 7 +#define EXCP08_DBLE 8 +#define EXCP09_XERR 9 +#define EXCP0A_TSS 10 +#define EXCP0B_NOSEG 11 +#define EXCP0C_STACK 12 +#define EXCP0D_GPF 13 +#define EXCP0E_PAGE 14 +#define EXCP10_COPR 16 +#define EXCP11_ALGN 17 +#define EXCP12_MCHK 18 + +enum { + CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ + CC_OP_EFLAGS, /* all cc are explicitely computed, CC_SRC = flags */ + + CC_OP_MULB, /* modify all flags, C, O = (CC_SRC != 0) */ + CC_OP_MULW, + CC_OP_MULL, + CC_OP_MULQ, + + CC_OP_ADDB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ + CC_OP_ADDW, + CC_OP_ADDL, + CC_OP_ADDQ, + + CC_OP_ADCB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ + CC_OP_ADCW, + CC_OP_ADCL, + CC_OP_ADCQ, + + CC_OP_SUBB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ + CC_OP_SUBW, + CC_OP_SUBL, + CC_OP_SUBQ, + + CC_OP_SBBB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ + CC_OP_SBBW, + CC_OP_SBBL, + CC_OP_SBBQ, + + CC_OP_LOGICB, /* modify all flags, CC_DST = res */ + CC_OP_LOGICW, + CC_OP_LOGICL, + CC_OP_LOGICQ, + + CC_OP_INCB, /* modify all flags except, CC_DST = res, CC_SRC = C */ + CC_OP_INCW, + CC_OP_INCL, + CC_OP_INCQ, + + CC_OP_DECB, /* modify all flags except, CC_DST = res, CC_SRC = C */ + CC_OP_DECW, + CC_OP_DECL, + CC_OP_DECQ, + + CC_OP_SHLB, /* modify all flags, CC_DST = res, CC_SRC.msb = C */ + CC_OP_SHLW, + CC_OP_SHLL, + CC_OP_SHLQ, + + CC_OP_SARB, /* modify all flags, CC_DST = res, CC_SRC.lsb = C */ + CC_OP_SARW, + CC_OP_SARL, + CC_OP_SARQ, + + CC_OP_NB, +}; + +#ifdef FLOATX80 +#define USE_X86LDOUBLE +#endif + +#ifdef USE_X86LDOUBLE +typedef floatx80 CPU86_LDouble; +#else +typedef float64 CPU86_LDouble; +#endif + +typedef struct SegmentCache { + uint32_t selector; + target_ulong base; + uint32_t limit; + uint32_t flags; +} SegmentCache; + +typedef union { + uint8_t _b[16]; + uint16_t _w[8]; + uint32_t _l[4]; + uint64_t _q[2]; + float32 _s[4]; + float64 _d[2]; +} XMMReg; + +typedef union { + uint8_t _b[8]; + uint16_t _w[2]; + uint32_t _l[1]; + uint64_t q; +} MMXReg; + +#ifdef WORDS_BIGENDIAN +#define XMM_B(n) _b[15 - (n)] +#define XMM_W(n) _w[7 - (n)] +#define XMM_L(n) _l[3 - (n)] +#define XMM_S(n) _s[3 - (n)] +#define XMM_Q(n) _q[1 - (n)] +#define XMM_D(n) _d[1 - (n)] + +#define MMX_B(n) _b[7 - (n)] +#define MMX_W(n) _w[3 - (n)] +#define MMX_L(n) _l[1 - (n)] +#else +#define XMM_B(n) _b[n] +#define XMM_W(n) _w[n] +#define XMM_L(n) _l[n] +#define XMM_S(n) _s[n] +#define XMM_Q(n) _q[n] +#define XMM_D(n) _d[n] + +#define MMX_B(n) _b[n] +#define MMX_W(n) _w[n] +#define MMX_L(n) _l[n] +#endif +#define MMX_Q(n) q + +#ifdef TARGET_X86_64 +#define CPU_NB_REGS 16 +#else +#define CPU_NB_REGS 8 +#endif + +typedef struct CPUX86State { +#if TARGET_LONG_BITS > HOST_LONG_BITS + /* temporaries if we cannot store them in host registers */ + target_ulong t0, t1, t2; +#endif + + /* standard registers */ + target_ulong regs[CPU_NB_REGS]; + target_ulong eip; + target_ulong eflags; /* eflags register. During CPU emulation, CC + flags and DF are set to zero because they are + stored elsewhere */ + + /* emulator internal eflags handling */ + target_ulong cc_src; + target_ulong cc_dst; + uint32_t cc_op; + int32_t df; /* D flag : 1 if D = 0, -1 if D = 1 */ + uint32_t hflags; /* hidden flags, see HF_xxx constants */ + + /* segments */ + SegmentCache segs[6]; /* selector values */ + SegmentCache ldt; + SegmentCache tr; + SegmentCache gdt; /* only base and limit are used */ + SegmentCache idt; /* only base and limit are used */ + + target_ulong cr[5]; /* NOTE: cr1 is unused */ + uint32_t a20_mask; + + /* FPU state */ + unsigned int fpstt; /* top of stack index */ + unsigned int fpus; + unsigned int fpuc; + uint8_t fptags[8]; /* 0 = valid, 1 = empty */ + union { +#ifdef USE_X86LDOUBLE + CPU86_LDouble d __attribute__((aligned(16))); +#else + CPU86_LDouble d; +#endif + MMXReg mmx; + } fpregs[8]; + + /* emulator internal variables */ + float_status fp_status; + CPU86_LDouble ft0; + union { + float f; + double d; + int i32; + int64_t i64; + } fp_convert; + + float_status sse_status; + uint32_t mxcsr; + XMMReg xmm_regs[CPU_NB_REGS]; + XMMReg xmm_t0; + MMXReg mmx_t0; + + /* sysenter registers */ + uint32_t sysenter_cs; + uint32_t sysenter_esp; + uint32_t sysenter_eip; + uint64_t efer; + uint64_t star; +#ifdef TARGET_X86_64 + target_ulong lstar; + target_ulong cstar; + target_ulong fmask; + target_ulong kernelgsbase; +#endif + + uint64_t pat; + + /* temporary data for USE_CODE_COPY mode */ +#ifdef USE_CODE_COPY + uint32_t tmp0; + uint32_t saved_esp; + int native_fp_regs; /* if true, the FPU state is in the native CPU regs */ +#endif + + /* exception/interrupt handling */ + jmp_buf jmp_env; + int exception_index; + int error_code; + int exception_is_int; + target_ulong exception_next_eip; + target_ulong dr[8]; /* debug registers */ + int interrupt_request; + int user_mode_only; /* user mode only simulation */ + + CPU_COMMON + + /* processor features (e.g. for CPUID insn) */ + uint32_t cpuid_level; + uint32_t cpuid_vendor1; + uint32_t cpuid_vendor2; + uint32_t cpuid_vendor3; + uint32_t cpuid_version; + uint32_t cpuid_features; + uint32_t cpuid_ext_features; + uint32_t cpuid_xlevel; + uint32_t cpuid_model[12]; + uint32_t cpuid_ext2_features; + +#ifdef USE_KQEMU + int kqemu_enabled; + int last_io_time; +#endif + /* in order to simplify APIC support, we leave this pointer to the + user */ + struct APICState *apic_state; +} CPUX86State; + +CPUX86State *cpu_x86_init(void); +int cpu_x86_exec(CPUX86State *s); +void cpu_x86_close(CPUX86State *s); +int cpu_get_pic_interrupt(CPUX86State *s); +/* MSDOS compatibility mode FPU exception support */ +void cpu_set_ferr(CPUX86State *s); + +/* this function must always be used to load data in the segment + cache: it synchronizes the hflags with the segment cache values */ +static inline void cpu_x86_load_seg_cache(CPUX86State *env, + int seg_reg, unsigned int selector, + uint32_t base, unsigned int limit, + unsigned int flags) +{ + SegmentCache *sc; + unsigned int new_hflags; + + sc = &env->segs[seg_reg]; + sc->selector = selector; + sc->base = base; + sc->limit = limit; + sc->flags = flags; + + /* update the hidden flags */ + { + if (seg_reg == R_CS) { +#ifdef TARGET_X86_64 + if ((env->hflags & HF_LMA_MASK) && (flags & DESC_L_MASK)) { + /* long mode */ + env->hflags |= HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK; + env->hflags &= ~(HF_ADDSEG_MASK); + } else +#endif + { + /* legacy / compatibility case */ + new_hflags = (env->segs[R_CS].flags & DESC_B_MASK) + >> (DESC_B_SHIFT - HF_CS32_SHIFT); + env->hflags = (env->hflags & ~(HF_CS32_MASK | HF_CS64_MASK)) | + new_hflags; + } + } + new_hflags = (env->segs[R_SS].flags & DESC_B_MASK) + >> (DESC_B_SHIFT - HF_SS32_SHIFT); + if (env->hflags & HF_CS64_MASK) { + /* zero base assumed for DS, ES and SS in long mode */ + } else if (!(env->cr[0] & CR0_PE_MASK) || + (env->eflags & VM_MASK) || + !(env->hflags & HF_CS32_MASK)) { + /* XXX: try to avoid this test. The problem comes from the + fact that is real mode or vm86 mode we only modify the + 'base' and 'selector' fields of the segment cache to go + faster. A solution may be to force addseg to one in + translate-i386.c. */ + new_hflags |= HF_ADDSEG_MASK; + } else { + new_hflags |= ((env->segs[R_DS].base | + env->segs[R_ES].base | + env->segs[R_SS].base) != 0) << + HF_ADDSEG_SHIFT; + } + env->hflags = (env->hflags & + ~(HF_SS32_MASK | HF_ADDSEG_MASK)) | new_hflags; + } +} + +/* wrapper, just in case memory mappings must be changed */ +static inline void cpu_x86_set_cpl(CPUX86State *s, int cpl) +{ +#if HF_CPL_MASK == 3 + s->hflags = (s->hflags & ~HF_CPL_MASK) | cpl; +#else +#error HF_CPL_MASK is hardcoded +#endif +} + +/* used for debug or cpu save/restore */ +void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f); +CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper); + +/* the following helpers are only usable in user mode simulation as + they can trigger unexpected exceptions */ +void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector); +void cpu_x86_fsave(CPUX86State *s, uint8_t *ptr, int data32); +void cpu_x86_frstor(CPUX86State *s, uint8_t *ptr, int data32); + +/* you can call this signal handler from your SIGBUS and SIGSEGV + signal handlers to inform the virtual CPU of exceptions. non zero + is returned if the signal was handled by the virtual CPU. */ +struct siginfo; +int cpu_x86_signal_handler(int host_signum, struct siginfo *info, + void *puc); +void cpu_x86_set_a20(CPUX86State *env, int a20_state); + +uint64_t cpu_get_tsc(CPUX86State *env); + +void cpu_set_apic_base(CPUX86State *env, uint64_t val); +uint64_t cpu_get_apic_base(CPUX86State *env); +void cpu_set_apic_tpr(CPUX86State *env, uint8_t val); +#ifndef NO_CPU_IO_DEFS +uint8_t cpu_get_apic_tpr(CPUX86State *env); +#endif + +/* will be suppressed */ +void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0); + +/* used to debug */ +#define X86_DUMP_FPU 0x0001 /* dump FPU state too */ +#define X86_DUMP_CCOP 0x0002 /* dump qemu flag cache */ + +#ifdef USE_KQEMU +static inline int cpu_get_time_fast(void) +{ + int low, high; + asm volatile("rdtsc" : "=a" (low), "=d" (high)); + return low; +} +#endif + +#define TARGET_PAGE_BITS 12 +#include "cpu-all.h" + +#endif /* CPU_I386_H */ diff --git a/tools/ioemu/target-i386/exec.h b/tools/ioemu/target-i386/exec.h new file mode 100644 index 0000000000..4ff527f841 --- /dev/null +++ b/tools/ioemu/target-i386/exec.h @@ -0,0 +1,572 @@ +/* + * i386 execution defines + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "config.h" +#include "dyngen-exec.h" + +/* XXX: factorize this mess */ +#ifdef TARGET_X86_64 +#define TARGET_LONG_BITS 64 +#else +#define TARGET_LONG_BITS 32 +#endif + +#include "cpu-defs.h" + +/* at least 4 register variables are defined */ +register struct CPUX86State *env asm(AREG0); + +#if TARGET_LONG_BITS > HOST_LONG_BITS + +/* no registers can be used */ +#define T0 (env->t0) +#define T1 (env->t1) +#define T2 (env->t2) + +#else + +/* XXX: use unsigned long instead of target_ulong - better code will + be generated for 64 bit CPUs */ +register target_ulong T0 asm(AREG1); +register target_ulong T1 asm(AREG2); +register target_ulong T2 asm(AREG3); + +/* if more registers are available, we define some registers too */ +#ifdef AREG4 +register target_ulong EAX asm(AREG4); +#define reg_EAX +#endif + +#ifdef AREG5 +register target_ulong ESP asm(AREG5); +#define reg_ESP +#endif + +#ifdef AREG6 +register target_ulong EBP asm(AREG6); +#define reg_EBP +#endif + +#ifdef AREG7 +register target_ulong ECX asm(AREG7); +#define reg_ECX +#endif + +#ifdef AREG8 +register target_ulong EDX asm(AREG8); +#define reg_EDX +#endif + +#ifdef AREG9 +register target_ulong EBX asm(AREG9); +#define reg_EBX +#endif + +#ifdef AREG10 +register target_ulong ESI asm(AREG10); +#define reg_ESI +#endif + +#ifdef AREG11 +register target_ulong EDI asm(AREG11); +#define reg_EDI +#endif + +#endif /* ! (TARGET_LONG_BITS > HOST_LONG_BITS) */ + +#define A0 T2 + +extern FILE *logfile; +extern int loglevel; + +#ifndef reg_EAX +#define EAX (env->regs[R_EAX]) +#endif +#ifndef reg_ECX +#define ECX (env->regs[R_ECX]) +#endif +#ifndef reg_EDX +#define EDX (env->regs[R_EDX]) +#endif +#ifndef reg_EBX +#define EBX (env->regs[R_EBX]) +#endif +#ifndef reg_ESP +#define ESP (env->regs[R_ESP]) +#endif +#ifndef reg_EBP +#define EBP (env->regs[R_EBP]) +#endif +#ifndef reg_ESI +#define ESI (env->regs[R_ESI]) +#endif +#ifndef reg_EDI +#define EDI (env->regs[R_EDI]) +#endif +#define EIP (env->eip) +#define DF (env->df) + +#define CC_SRC (env->cc_src) +#define CC_DST (env->cc_dst) +#define CC_OP (env->cc_op) + +/* float macros */ +#define FT0 (env->ft0) +#define ST0 (env->fpregs[env->fpstt].d) +#define ST(n) (env->fpregs[(env->fpstt + (n)) & 7].d) +#define ST1 ST(1) + +#ifdef USE_FP_CONVERT +#define FP_CONVERT (env->fp_convert) +#endif + +#include "cpu.h" +#include "exec-all.h" + +typedef struct CCTable { + int (*compute_all)(void); /* return all the flags */ + int (*compute_c)(void); /* return the C flag */ +} CCTable; + +extern CCTable cc_table[]; + +void load_seg(int seg_reg, int selector); +void helper_ljmp_protected_T0_T1(int next_eip); +void helper_lcall_real_T0_T1(int shift, int next_eip); +void helper_lcall_protected_T0_T1(int shift, int next_eip); +void helper_iret_real(int shift); +void helper_iret_protected(int shift, int next_eip); +void helper_lret_protected(int shift, int addend); +void helper_lldt_T0(void); +void helper_ltr_T0(void); +void helper_movl_crN_T0(int reg); +void helper_movl_drN_T0(int reg); +void helper_invlpg(target_ulong addr); +void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0); +void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3); +void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4); +void cpu_x86_flush_tlb(CPUX86State *env, target_ulong addr); +int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, + int is_write, int is_user, int is_softmmu); +void tlb_fill(target_ulong addr, int is_write, int is_user, + void *retaddr); +void __hidden cpu_lock(void); +void __hidden cpu_unlock(void); +void do_interrupt(int intno, int is_int, int error_code, + target_ulong next_eip, int is_hw); +void do_interrupt_user(int intno, int is_int, int error_code, + target_ulong next_eip); +void raise_interrupt(int intno, int is_int, int error_code, + int next_eip_addend); +void raise_exception_err(int exception_index, int error_code); +void raise_exception(int exception_index); +void __hidden cpu_loop_exit(void); + +void OPPROTO op_movl_eflags_T0(void); +void OPPROTO op_movl_T0_eflags(void); +void helper_divl_EAX_T0(void); +void helper_idivl_EAX_T0(void); +void helper_mulq_EAX_T0(void); +void helper_imulq_EAX_T0(void); +void helper_imulq_T0_T1(void); +void helper_divq_EAX_T0(void); +void helper_idivq_EAX_T0(void); +void helper_bswapq_T0(void); +void helper_cmpxchg8b(void); +void helper_cpuid(void); +void helper_enter_level(int level, int data32); +void helper_enter64_level(int level, int data64); +void helper_sysenter(void); +void helper_sysexit(void); +void helper_syscall(int next_eip_addend); +void helper_sysret(int dflag); +void helper_rdtsc(void); +void helper_rdmsr(void); +void helper_wrmsr(void); +void helper_lsl(void); +void helper_lar(void); +void helper_verr(void); +void helper_verw(void); + +void check_iob_T0(void); +void check_iow_T0(void); +void check_iol_T0(void); +void check_iob_DX(void); +void check_iow_DX(void); +void check_iol_DX(void); + +#if !defined(CONFIG_USER_ONLY) + +#include "softmmu_exec.h" + +static inline double ldfq(target_ulong ptr) +{ + union { + double d; + uint64_t i; + } u; + u.i = ldq(ptr); + return u.d; +} + +static inline void stfq(target_ulong ptr, double v) +{ + union { + double d; + uint64_t i; + } u; + u.d = v; + stq(ptr, u.i); +} + +static inline float ldfl(target_ulong ptr) +{ + union { + float f; + uint32_t i; + } u; + u.i = ldl(ptr); + return u.f; +} + +static inline void stfl(target_ulong ptr, float v) +{ + union { + float f; + uint32_t i; + } u; + u.f = v; + stl(ptr, u.i); +} + +#endif /* !defined(CONFIG_USER_ONLY) */ + +#ifdef USE_X86LDOUBLE +/* use long double functions */ +#define floatx_to_int32 floatx80_to_int32 +#define floatx_to_int64 floatx80_to_int64 +#define floatx_to_int32_round_to_zero floatx80_to_int32_round_to_zero +#define floatx_to_int64_round_to_zero floatx80_to_int64_round_to_zero +#define floatx_abs floatx80_abs +#define floatx_chs floatx80_chs +#define floatx_round_to_int floatx80_round_to_int +#define floatx_compare floatx80_compare +#define floatx_compare_quiet floatx80_compare_quiet +#define sin sinl +#define cos cosl +#define sqrt sqrtl +#define pow powl +#define log logl +#define tan tanl +#define atan2 atan2l +#define floor floorl +#define ceil ceill +#define ldexp ldexpl +#else +#define floatx_to_int32 float64_to_int32 +#define floatx_to_int64 float64_to_int64 +#define floatx_to_int32_round_to_zero float64_to_int32_round_to_zero +#define floatx_to_int64_round_to_zero float64_to_int64_round_to_zero +#define floatx_abs float64_abs +#define floatx_chs float64_chs +#define floatx_round_to_int float64_round_to_int +#define floatx_compare float64_compare +#define floatx_compare_quiet float64_compare_quiet +#endif + +extern CPU86_LDouble sin(CPU86_LDouble x); +extern CPU86_LDouble cos(CPU86_LDouble x); +extern CPU86_LDouble sqrt(CPU86_LDouble x); +extern CPU86_LDouble pow(CPU86_LDouble, CPU86_LDouble); +extern CPU86_LDouble log(CPU86_LDouble x); +extern CPU86_LDouble tan(CPU86_LDouble x); +extern CPU86_LDouble atan2(CPU86_LDouble, CPU86_LDouble); +extern CPU86_LDouble floor(CPU86_LDouble x); +extern CPU86_LDouble ceil(CPU86_LDouble x); + +#define RC_MASK 0xc00 +#define RC_NEAR 0x000 +#define RC_DOWN 0x400 +#define RC_UP 0x800 +#define RC_CHOP 0xc00 + +#define MAXTAN 9223372036854775808.0 + +#ifdef USE_X86LDOUBLE + +/* only for x86 */ +typedef union { + long double d; + struct { + unsigned long long lower; + unsigned short upper; + } l; +} CPU86_LDoubleU; + +/* the following deal with x86 long double-precision numbers */ +#define MAXEXPD 0x7fff +#define EXPBIAS 16383 +#define EXPD(fp) (fp.l.upper & 0x7fff) +#define SIGND(fp) ((fp.l.upper) & 0x8000) +#define MANTD(fp) (fp.l.lower) +#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7fff)) | EXPBIAS + +#else + +/* NOTE: arm is horrible as double 32 bit words are stored in big endian ! */ +typedef union { + double d; +#if !defined(WORDS_BIGENDIAN) && !defined(__arm__) + struct { + uint32_t lower; + int32_t upper; + } l; +#else + struct { + int32_t upper; + uint32_t lower; + } l; +#endif +#ifndef __arm__ + int64_t ll; +#endif +} CPU86_LDoubleU; + +/* the following deal with IEEE double-precision numbers */ +#define MAXEXPD 0x7ff +#define EXPBIAS 1023 +#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF) +#define SIGND(fp) ((fp.l.upper) & 0x80000000) +#ifdef __arm__ +#define MANTD(fp) (fp.l.lower | ((uint64_t)(fp.l.upper & ((1 << 20) - 1)) << 32)) +#else +#define MANTD(fp) (fp.ll & ((1LL << 52) - 1)) +#endif +#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7ff << 20)) | (EXPBIAS << 20) +#endif + +static inline void fpush(void) +{ + env->fpstt = (env->fpstt - 1) & 7; + env->fptags[env->fpstt] = 0; /* validate stack entry */ +} + +static inline void fpop(void) +{ + env->fptags[env->fpstt] = 1; /* invvalidate stack entry */ + env->fpstt = (env->fpstt + 1) & 7; +} + +#ifndef USE_X86LDOUBLE +static inline CPU86_LDouble helper_fldt(target_ulong ptr) +{ + CPU86_LDoubleU temp; + int upper, e; + uint64_t ll; + + /* mantissa */ + upper = lduw(ptr + 8); + /* XXX: handle overflow ? */ + e = (upper & 0x7fff) - 16383 + EXPBIAS; /* exponent */ + e |= (upper >> 4) & 0x800; /* sign */ + ll = (ldq(ptr) >> 11) & ((1LL << 52) - 1); +#ifdef __arm__ + temp.l.upper = (e << 20) | (ll >> 32); + temp.l.lower = ll; +#else + temp.ll = ll | ((uint64_t)e << 52); +#endif + return temp.d; +} + +static inline void helper_fstt(CPU86_LDouble f, target_ulong ptr) +{ + CPU86_LDoubleU temp; + int e; + + temp.d = f; + /* mantissa */ + stq(ptr, (MANTD(temp) << 11) | (1LL << 63)); + /* exponent + sign */ + e = EXPD(temp) - EXPBIAS + 16383; + e |= SIGND(temp) >> 16; + stw(ptr + 8, e); +} +#else + +/* XXX: same endianness assumed */ + +#ifdef CONFIG_USER_ONLY + +static inline CPU86_LDouble helper_fldt(target_ulong ptr) +{ + return *(CPU86_LDouble *)ptr; +} + +static inline void helper_fstt(CPU86_LDouble f, target_ulong ptr) +{ + *(CPU86_LDouble *)ptr = f; +} + +#else + +/* we use memory access macros */ + +static inline CPU86_LDouble helper_fldt(target_ulong ptr) +{ + CPU86_LDoubleU temp; + + temp.l.lower = ldq(ptr); + temp.l.upper = lduw(ptr + 8); + return temp.d; +} + +static inline void helper_fstt(CPU86_LDouble f, target_ulong ptr) +{ + CPU86_LDoubleU temp; + + temp.d = f; + stq(ptr, temp.l.lower); + stw(ptr + 8, temp.l.upper); +} + +#endif /* !CONFIG_USER_ONLY */ + +#endif /* USE_X86LDOUBLE */ + +#define FPUS_IE (1 << 0) +#define FPUS_DE (1 << 1) +#define FPUS_ZE (1 << 2) +#define FPUS_OE (1 << 3) +#define FPUS_UE (1 << 4) +#define FPUS_PE (1 << 5) +#define FPUS_SF (1 << 6) +#define FPUS_SE (1 << 7) +#define FPUS_B (1 << 15) + +#define FPUC_EM 0x3f + +extern const CPU86_LDouble f15rk[7]; + +void helper_fldt_ST0_A0(void); +void helper_fstt_ST0_A0(void); +void fpu_raise_exception(void); +CPU86_LDouble helper_fdiv(CPU86_LDouble a, CPU86_LDouble b); +void helper_fbld_ST0_A0(void); +void helper_fbst_ST0_A0(void); +void helper_f2xm1(void); +void helper_fyl2x(void); +void helper_fptan(void); +void helper_fpatan(void); +void helper_fxtract(void); +void helper_fprem1(void); +void helper_fprem(void); +void helper_fyl2xp1(void); +void helper_fsqrt(void); +void helper_fsincos(void); +void helper_frndint(void); +void helper_fscale(void); +void helper_fsin(void); +void helper_fcos(void); +void helper_fxam_ST0(void); +void helper_fstenv(target_ulong ptr, int data32); +void helper_fldenv(target_ulong ptr, int data32); +void helper_fsave(target_ulong ptr, int data32); +void helper_frstor(target_ulong ptr, int data32); +void helper_fxsave(target_ulong ptr, int data64); +void helper_fxrstor(target_ulong ptr, int data64); +void restore_native_fp_state(CPUState *env); +void save_native_fp_state(CPUState *env); +float approx_rsqrt(float a); +float approx_rcp(float a); +void update_fp_status(void); + +extern const uint8_t parity_table[256]; +extern const uint8_t rclw_table[32]; +extern const uint8_t rclb_table[32]; + +static inline uint32_t compute_eflags(void) +{ + return env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK); +} + +/* NOTE: CC_OP must be modified manually to CC_OP_EFLAGS */ +static inline void load_eflags(int eflags, int update_mask) +{ + CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + DF = 1 - (2 * ((eflags >> 10) & 1)); + env->eflags = (env->eflags & ~update_mask) | + (eflags & update_mask); +} + +static inline void env_to_regs(void) +{ +#ifdef reg_EAX + EAX = env->regs[R_EAX]; +#endif +#ifdef reg_ECX + ECX = env->regs[R_ECX]; +#endif +#ifdef reg_EDX + EDX = env->regs[R_EDX]; +#endif +#ifdef reg_EBX + EBX = env->regs[R_EBX]; +#endif +#ifdef reg_ESP + ESP = env->regs[R_ESP]; +#endif +#ifdef reg_EBP + EBP = env->regs[R_EBP]; +#endif +#ifdef reg_ESI + ESI = env->regs[R_ESI]; +#endif +#ifdef reg_EDI + EDI = env->regs[R_EDI]; +#endif +} + +static inline void regs_to_env(void) +{ +#ifdef reg_EAX + env->regs[R_EAX] = EAX; +#endif +#ifdef reg_ECX + env->regs[R_ECX] = ECX; +#endif +#ifdef reg_EDX + env->regs[R_EDX] = EDX; +#endif +#ifdef reg_EBX + env->regs[R_EBX] = EBX; +#endif +#ifdef reg_ESP + env->regs[R_ESP] = ESP; +#endif +#ifdef reg_EBP + env->regs[R_EBP] = EBP; +#endif +#ifdef reg_ESI + env->regs[R_ESI] = ESI; +#endif +#ifdef reg_EDI + env->regs[R_EDI] = EDI; +#endif +} diff --git a/tools/ioemu/target-i386/helper.c b/tools/ioemu/target-i386/helper.c new file mode 100644 index 0000000000..123f510497 --- /dev/null +++ b/tools/ioemu/target-i386/helper.c @@ -0,0 +1,3505 @@ +/* + * i386 helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "exec.h" + +//#define DEBUG_PCALL + +#if 0 +#define raise_exception_err(a, b)\ +do {\ + if (logfile)\ + fprintf(logfile, "raise_exception line=%d\n", __LINE__);\ + (raise_exception_err)(a, b);\ +} while (0) +#endif + +const uint8_t parity_table[256] = { + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +}; + +/* modulo 17 table */ +const uint8_t rclw_table[32] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9,10,11,12,13,14,15, + 16, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9,10,11,12,13,14, +}; + +/* modulo 9 table */ +const uint8_t rclb_table[32] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 0, 1, 2, 3, 4, +}; + +const CPU86_LDouble f15rk[7] = +{ + 0.00000000000000000000L, + 1.00000000000000000000L, + 3.14159265358979323851L, /*pi*/ + 0.30102999566398119523L, /*lg2*/ + 0.69314718055994530943L, /*ln2*/ + 1.44269504088896340739L, /*l2e*/ + 3.32192809488736234781L, /*l2t*/ +}; + +/* thread support */ + +spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED; + +void cpu_lock(void) +{ + spin_lock(&global_cpu_lock); +} + +void cpu_unlock(void) +{ + spin_unlock(&global_cpu_lock); +} + +void cpu_loop_exit(void) +{ + /* NOTE: the register at this point must be saved by hand because + longjmp restore them */ + regs_to_env(); + longjmp(env->jmp_env, 1); +} + +/* return non zero if error */ +static inline int load_segment(uint32_t *e1_ptr, uint32_t *e2_ptr, + int selector) +{ + SegmentCache *dt; + int index; + target_ulong ptr; + + if (selector & 0x4) + dt = &env->ldt; + else + dt = &env->gdt; + index = selector & ~7; + if ((index + 7) > dt->limit) + return -1; + ptr = dt->base + index; + *e1_ptr = ldl_kernel(ptr); + *e2_ptr = ldl_kernel(ptr + 4); + return 0; +} + +static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2) +{ + unsigned int limit; + limit = (e1 & 0xffff) | (e2 & 0x000f0000); + if (e2 & DESC_G_MASK) + limit = (limit << 12) | 0xfff; + return limit; +} + +static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2) +{ + return ((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000)); +} + +static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, uint32_t e2) +{ + sc->base = get_seg_base(e1, e2); + sc->limit = get_seg_limit(e1, e2); + sc->flags = e2; +} + +/* init the segment cache in vm86 mode. */ +static inline void load_seg_vm(int seg, int selector) +{ + selector &= 0xffff; + cpu_x86_load_seg_cache(env, seg, selector, + (selector << 4), 0xffff, 0); +} + +static inline void get_ss_esp_from_tss(uint32_t *ss_ptr, + uint32_t *esp_ptr, int dpl) +{ + int type, index, shift; + +#if 0 + { + int i; + printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit); + for(i=0;itr.limit;i++) { + printf("%02x ", env->tr.base[i]); + if ((i & 7) == 7) printf("\n"); + } + printf("\n"); + } +#endif + + if (!(env->tr.flags & DESC_P_MASK)) + cpu_abort(env, "invalid tss"); + type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; + if ((type & 7) != 1) + cpu_abort(env, "invalid tss type"); + shift = type >> 3; + index = (dpl * 4 + 2) << shift; + if (index + (4 << shift) - 1 > env->tr.limit) + raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc); + if (shift == 0) { + *esp_ptr = lduw_kernel(env->tr.base + index); + *ss_ptr = lduw_kernel(env->tr.base + index + 2); + } else { + *esp_ptr = ldl_kernel(env->tr.base + index); + *ss_ptr = lduw_kernel(env->tr.base + index + 4); + } +} + +/* XXX: merge with load_seg() */ +static void tss_load_seg(int seg_reg, int selector) +{ + uint32_t e1, e2; + int rpl, dpl, cpl; + + if ((selector & 0xfffc) != 0) { + if (load_segment(&e1, &e2, selector) != 0) + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); + if (!(e2 & DESC_S_MASK)) + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); + rpl = selector & 3; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + if (seg_reg == R_CS) { + if (!(e2 & DESC_CS_MASK)) + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); + /* XXX: is it correct ? */ + if (dpl != rpl) + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); + if ((e2 & DESC_C_MASK) && dpl > rpl) + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); + } else if (seg_reg == R_SS) { + /* SS must be writable data */ + if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); + if (dpl != cpl || dpl != rpl) + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); + } else { + /* not readable code */ + if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK)) + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); + /* if data or non conforming code, checks the rights */ + if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) { + if (dpl < cpl || dpl < rpl) + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); + } + } + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc); + cpu_x86_load_seg_cache(env, seg_reg, selector, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); + } else { + if (seg_reg == R_SS || seg_reg == R_CS) + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); + } +} + +#define SWITCH_TSS_JMP 0 +#define SWITCH_TSS_IRET 1 +#define SWITCH_TSS_CALL 2 + +/* XXX: restore CPU state in registers (PowerPC case) */ +static void switch_tss(int tss_selector, + uint32_t e1, uint32_t e2, int source, + uint32_t next_eip) +{ + int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i; + target_ulong tss_base; + uint32_t new_regs[8], new_segs[6]; + uint32_t new_eflags, new_eip, new_cr3, new_ldt, new_trap; + uint32_t old_eflags, eflags_mask; + SegmentCache *dt; + int index; + target_ulong ptr; + + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; +#ifdef DEBUG_PCALL + if (loglevel & CPU_LOG_PCALL) + fprintf(logfile, "switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, source); +#endif + + /* if task gate, we read the TSS segment and we load it */ + if (type == 5) { + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc); + tss_selector = e1 >> 16; + if (tss_selector & 4) + raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc); + if (load_segment(&e1, &e2, tss_selector) != 0) + raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc); + if (e2 & DESC_S_MASK) + raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc); + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; + if ((type & 7) != 1) + raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc); + } + + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc); + + if (type & 8) + tss_limit_max = 103; + else + tss_limit_max = 43; + tss_limit = get_seg_limit(e1, e2); + tss_base = get_seg_base(e1, e2); + if ((tss_selector & 4) != 0 || + tss_limit < tss_limit_max) + raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc); + old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; + if (old_type & 8) + old_tss_limit_max = 103; + else + old_tss_limit_max = 43; + + /* read all the registers from the new TSS */ + if (type & 8) { + /* 32 bit */ + new_cr3 = ldl_kernel(tss_base + 0x1c); + new_eip = ldl_kernel(tss_base + 0x20); + new_eflags = ldl_kernel(tss_base + 0x24); + for(i = 0; i < 8; i++) + new_regs[i] = ldl_kernel(tss_base + (0x28 + i * 4)); + for(i = 0; i < 6; i++) + new_segs[i] = lduw_kernel(tss_base + (0x48 + i * 4)); + new_ldt = lduw_kernel(tss_base + 0x60); + new_trap = ldl_kernel(tss_base + 0x64); + } else { + /* 16 bit */ + new_cr3 = 0; + new_eip = lduw_kernel(tss_base + 0x0e); + new_eflags = lduw_kernel(tss_base + 0x10); + for(i = 0; i < 8; i++) + new_regs[i] = lduw_kernel(tss_base + (0x12 + i * 2)) | 0xffff0000; + for(i = 0; i < 4; i++) + new_segs[i] = lduw_kernel(tss_base + (0x22 + i * 4)); + new_ldt = lduw_kernel(tss_base + 0x2a); + new_segs[R_FS] = 0; + new_segs[R_GS] = 0; + new_trap = 0; + } + + /* NOTE: we must avoid memory exceptions during the task switch, + so we make dummy accesses before */ + /* XXX: it can still fail in some cases, so a bigger hack is + necessary to valid the TLB after having done the accesses */ + + v1 = ldub_kernel(env->tr.base); + v2 = ldub_kernel(env->tr.base + old_tss_limit_max); + stb_kernel(env->tr.base, v1); + stb_kernel(env->tr.base + old_tss_limit_max, v2); + + /* clear busy bit (it is restartable) */ + if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) { + target_ulong ptr; + uint32_t e2; + ptr = env->gdt.base + (env->tr.selector & ~7); + e2 = ldl_kernel(ptr + 4); + e2 &= ~DESC_TSS_BUSY_MASK; + stl_kernel(ptr + 4, e2); + } + old_eflags = compute_eflags(); + if (source == SWITCH_TSS_IRET) + old_eflags &= ~NT_MASK; + + /* save the current state in the old TSS */ + if (type & 8) { + /* 32 bit */ + stl_kernel(env->tr.base + 0x20, next_eip); + stl_kernel(env->tr.base + 0x24, old_eflags); + stl_kernel(env->tr.base + (0x28 + 0 * 4), EAX); + stl_kernel(env->tr.base + (0x28 + 1 * 4), ECX); + stl_kernel(env->tr.base + (0x28 + 2 * 4), EDX); + stl_kernel(env->tr.base + (0x28 + 3 * 4), EBX); + stl_kernel(env->tr.base + (0x28 + 4 * 4), ESP); + stl_kernel(env->tr.base + (0x28 + 5 * 4), EBP); + stl_kernel(env->tr.base + (0x28 + 6 * 4), ESI); + stl_kernel(env->tr.base + (0x28 + 7 * 4), EDI); + for(i = 0; i < 6; i++) + stw_kernel(env->tr.base + (0x48 + i * 4), env->segs[i].selector); + } else { + /* 16 bit */ + stw_kernel(env->tr.base + 0x0e, next_eip); + stw_kernel(env->tr.base + 0x10, old_eflags); + stw_kernel(env->tr.base + (0x12 + 0 * 2), EAX); + stw_kernel(env->tr.base + (0x12 + 1 * 2), ECX); + stw_kernel(env->tr.base + (0x12 + 2 * 2), EDX); + stw_kernel(env->tr.base + (0x12 + 3 * 2), EBX); + stw_kernel(env->tr.base + (0x12 + 4 * 2), ESP); + stw_kernel(env->tr.base + (0x12 + 5 * 2), EBP); + stw_kernel(env->tr.base + (0x12 + 6 * 2), ESI); + stw_kernel(env->tr.base + (0x12 + 7 * 2), EDI); + for(i = 0; i < 4; i++) + stw_kernel(env->tr.base + (0x22 + i * 4), env->segs[i].selector); + } + + /* now if an exception occurs, it will occurs in the next task + context */ + + if (source == SWITCH_TSS_CALL) { + stw_kernel(tss_base, env->tr.selector); + new_eflags |= NT_MASK; + } + + /* set busy bit */ + if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) { + target_ulong ptr; + uint32_t e2; + ptr = env->gdt.base + (tss_selector & ~7); + e2 = ldl_kernel(ptr + 4); + e2 |= DESC_TSS_BUSY_MASK; + stl_kernel(ptr + 4, e2); + } + + /* set the new CPU state */ + /* from this point, any exception which occurs can give problems */ + env->cr[0] |= CR0_TS_MASK; + env->hflags |= HF_TS_MASK; + env->tr.selector = tss_selector; + env->tr.base = tss_base; + env->tr.limit = tss_limit; + env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK; + + if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) { + cpu_x86_update_cr3(env, new_cr3); + } + + /* load all registers without an exception, then reload them with + possible exception */ + env->eip = new_eip; + eflags_mask = TF_MASK | AC_MASK | ID_MASK | + IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK; + if (!(type & 8)) + eflags_mask &= 0xffff; + load_eflags(new_eflags, eflags_mask); + /* XXX: what to do in 16 bit case ? */ + EAX = new_regs[0]; + ECX = new_regs[1]; + EDX = new_regs[2]; + EBX = new_regs[3]; + ESP = new_regs[4]; + EBP = new_regs[5]; + ESI = new_regs[6]; + EDI = new_regs[7]; + if (new_eflags & VM_MASK) { + for(i = 0; i < 6; i++) + load_seg_vm(i, new_segs[i]); + /* in vm86, CPL is always 3 */ + cpu_x86_set_cpl(env, 3); + } else { + /* CPL is set the RPL of CS */ + cpu_x86_set_cpl(env, new_segs[R_CS] & 3); + /* first just selectors as the rest may trigger exceptions */ + for(i = 0; i < 6; i++) + cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0); + } + + env->ldt.selector = new_ldt & ~4; + env->ldt.base = 0; + env->ldt.limit = 0; + env->ldt.flags = 0; + + /* load the LDT */ + if (new_ldt & 4) + raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc); + + if ((new_ldt & 0xfffc) != 0) { + dt = &env->gdt; + index = new_ldt & ~7; + if ((index + 7) > dt->limit) + raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc); + ptr = dt->base + index; + e1 = ldl_kernel(ptr); + e2 = ldl_kernel(ptr + 4); + if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) + raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc); + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc); + load_seg_cache_raw_dt(&env->ldt, e1, e2); + } + + /* load the segments */ + if (!(new_eflags & VM_MASK)) { + tss_load_seg(R_CS, new_segs[R_CS]); + tss_load_seg(R_SS, new_segs[R_SS]); + tss_load_seg(R_ES, new_segs[R_ES]); + tss_load_seg(R_DS, new_segs[R_DS]); + tss_load_seg(R_FS, new_segs[R_FS]); + tss_load_seg(R_GS, new_segs[R_GS]); + } + + /* check that EIP is in the CS segment limits */ + if (new_eip > env->segs[R_CS].limit) { + /* XXX: different exception if CALL ? */ + raise_exception_err(EXCP0D_GPF, 0); + } +} + +/* check if Port I/O is allowed in TSS */ +static inline void check_io(int addr, int size) +{ + int io_offset, val, mask; + + /* TSS must be a valid 32 bit one */ + if (!(env->tr.flags & DESC_P_MASK) || + ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 || + env->tr.limit < 103) + goto fail; + io_offset = lduw_kernel(env->tr.base + 0x66); + io_offset += (addr >> 3); + /* Note: the check needs two bytes */ + if ((io_offset + 1) > env->tr.limit) + goto fail; + val = lduw_kernel(env->tr.base + io_offset); + val >>= (addr & 7); + mask = (1 << size) - 1; + /* all bits must be zero to allow the I/O */ + if ((val & mask) != 0) { + fail: + raise_exception_err(EXCP0D_GPF, 0); + } +} + +void check_iob_T0(void) +{ + check_io(T0, 1); +} + +void check_iow_T0(void) +{ + check_io(T0, 2); +} + +void check_iol_T0(void) +{ + check_io(T0, 4); +} + +void check_iob_DX(void) +{ + check_io(EDX & 0xffff, 1); +} + +void check_iow_DX(void) +{ + check_io(EDX & 0xffff, 2); +} + +void check_iol_DX(void) +{ + check_io(EDX & 0xffff, 4); +} + +static inline unsigned int get_sp_mask(unsigned int e2) +{ + if (e2 & DESC_B_MASK) + return 0xffffffff; + else + return 0xffff; +} + +/* XXX: add a is_user flag to have proper security support */ +#define PUSHW(ssp, sp, sp_mask, val)\ +{\ + sp -= 2;\ + stw_kernel((ssp) + (sp & (sp_mask)), (val));\ +} + +#define PUSHL(ssp, sp, sp_mask, val)\ +{\ + sp -= 4;\ + stl_kernel((ssp) + (sp & (sp_mask)), (val));\ +} + +#define POPW(ssp, sp, sp_mask, val)\ +{\ + val = lduw_kernel((ssp) + (sp & (sp_mask)));\ + sp += 2;\ +} + +#define POPL(ssp, sp, sp_mask, val)\ +{\ + val = (uint32_t)ldl_kernel((ssp) + (sp & (sp_mask)));\ + sp += 4;\ +} + +/* protected mode interrupt */ +static void do_interrupt_protected(int intno, int is_int, int error_code, + unsigned int next_eip, int is_hw) +{ + SegmentCache *dt; + target_ulong ptr, ssp; + int type, dpl, selector, ss_dpl, cpl, sp_mask; + int has_error_code, new_stack, shift; + uint32_t e1, e2, offset, ss, esp, ss_e1, ss_e2; + uint32_t old_eip; + + has_error_code = 0; + if (!is_int && !is_hw) { + switch(intno) { + case 8: + case 10: + case 11: + case 12: + case 13: + case 14: + case 17: + has_error_code = 1; + break; + } + } + if (is_int) + old_eip = next_eip; + else + old_eip = env->eip; + + dt = &env->idt; + if (intno * 8 + 7 > dt->limit) + raise_exception_err(EXCP0D_GPF, intno * 8 + 2); + ptr = dt->base + intno * 8; + e1 = ldl_kernel(ptr); + e2 = ldl_kernel(ptr + 4); + /* check gate type */ + type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; + switch(type) { + case 5: /* task gate */ + /* must do that check here to return the correct error code */ + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2); + switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip); + if (has_error_code) { + int mask, type; + /* push the error code */ + type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; + shift = type >> 3; + if (env->segs[R_SS].flags & DESC_B_MASK) + mask = 0xffffffff; + else + mask = 0xffff; + esp = (ESP - (2 << shift)) & mask; + ssp = env->segs[R_SS].base + esp; + if (shift) + stl_kernel(ssp, error_code); + else + stw_kernel(ssp, error_code); + ESP = (esp & mask) | (ESP & ~mask); + } + return; + case 6: /* 286 interrupt gate */ + case 7: /* 286 trap gate */ + case 14: /* 386 interrupt gate */ + case 15: /* 386 trap gate */ + break; + default: + raise_exception_err(EXCP0D_GPF, intno * 8 + 2); + break; + } + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + /* check privledge if software int */ + if (is_int && dpl < cpl) + raise_exception_err(EXCP0D_GPF, intno * 8 + 2); + /* check valid bit */ + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2); + selector = e1 >> 16; + offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); + if ((selector & 0xfffc) == 0) + raise_exception_err(EXCP0D_GPF, 0); + + if (load_segment(&e1, &e2, selector) != 0) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (dpl > cpl) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc); + if (!(e2 & DESC_C_MASK) && dpl < cpl) { + /* to inner priviledge */ + get_ss_esp_from_tss(&ss, &esp, dpl); + if ((ss & 0xfffc) == 0) + raise_exception_err(EXCP0A_TSS, ss & 0xfffc); + if ((ss & 3) != dpl) + raise_exception_err(EXCP0A_TSS, ss & 0xfffc); + if (load_segment(&ss_e1, &ss_e2, ss) != 0) + raise_exception_err(EXCP0A_TSS, ss & 0xfffc); + ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; + if (ss_dpl != dpl) + raise_exception_err(EXCP0A_TSS, ss & 0xfffc); + if (!(ss_e2 & DESC_S_MASK) || + (ss_e2 & DESC_CS_MASK) || + !(ss_e2 & DESC_W_MASK)) + raise_exception_err(EXCP0A_TSS, ss & 0xfffc); + if (!(ss_e2 & DESC_P_MASK)) + raise_exception_err(EXCP0A_TSS, ss & 0xfffc); + new_stack = 1; + sp_mask = get_sp_mask(ss_e2); + ssp = get_seg_base(ss_e1, ss_e2); + } else if ((e2 & DESC_C_MASK) || dpl == cpl) { + /* to same priviledge */ + if (env->eflags & VM_MASK) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + new_stack = 0; + sp_mask = get_sp_mask(env->segs[R_SS].flags); + ssp = env->segs[R_SS].base; + esp = ESP; + dpl = cpl; + } else { + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + new_stack = 0; /* avoid warning */ + sp_mask = 0; /* avoid warning */ + ssp = 0; /* avoid warning */ + esp = 0; /* avoid warning */ + } + + shift = type >> 3; + +#if 0 + /* XXX: check that enough room is available */ + push_size = 6 + (new_stack << 2) + (has_error_code << 1); + if (env->eflags & VM_MASK) + push_size += 8; + push_size <<= shift; +#endif + if (shift == 1) { + if (new_stack) { + if (env->eflags & VM_MASK) { + PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector); + PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector); + PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector); + PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector); + } + PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector); + PUSHL(ssp, esp, sp_mask, ESP); + } + PUSHL(ssp, esp, sp_mask, compute_eflags()); + PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector); + PUSHL(ssp, esp, sp_mask, old_eip); + if (has_error_code) { + PUSHL(ssp, esp, sp_mask, error_code); + } + } else { + if (new_stack) { + if (env->eflags & VM_MASK) { + PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector); + PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector); + PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector); + PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector); + } + PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector); + PUSHW(ssp, esp, sp_mask, ESP); + } + PUSHW(ssp, esp, sp_mask, compute_eflags()); + PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector); + PUSHW(ssp, esp, sp_mask, old_eip); + if (has_error_code) { + PUSHW(ssp, esp, sp_mask, error_code); + } + } + + if (new_stack) { + if (env->eflags & VM_MASK) { + cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0); + cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0); + cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0); + cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0); + } + ss = (ss & ~3) | dpl; + cpu_x86_load_seg_cache(env, R_SS, ss, + ssp, get_seg_limit(ss_e1, ss_e2), ss_e2); + } + ESP = (ESP & ~sp_mask) | (esp & sp_mask); + + selector = (selector & ~3) | dpl; + cpu_x86_load_seg_cache(env, R_CS, selector, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); + cpu_x86_set_cpl(env, dpl); + env->eip = offset; + + /* interrupt gate clear IF mask */ + if ((type & 1) == 0) { + env->eflags &= ~IF_MASK; + } + env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK); +} + +#ifdef TARGET_X86_64 + +#define PUSHQ(sp, val)\ +{\ + sp -= 8;\ + stq_kernel(sp, (val));\ +} + +#define POPQ(sp, val)\ +{\ + val = ldq_kernel(sp);\ + sp += 8;\ +} + +static inline target_ulong get_rsp_from_tss(int level) +{ + int index; + +#if 0 + printf("TR: base=" TARGET_FMT_lx " limit=%x\n", + env->tr.base, env->tr.limit); +#endif + + if (!(env->tr.flags & DESC_P_MASK)) + cpu_abort(env, "invalid tss"); + index = 8 * level + 4; + if ((index + 7) > env->tr.limit) + raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc); + return ldq_kernel(env->tr.base + index); +} + +/* 64 bit interrupt */ +static void do_interrupt64(int intno, int is_int, int error_code, + target_ulong next_eip, int is_hw) +{ + SegmentCache *dt; + target_ulong ptr; + int type, dpl, selector, cpl, ist; + int has_error_code, new_stack; + uint32_t e1, e2, e3, ss; + target_ulong old_eip, esp, offset; + + has_error_code = 0; + if (!is_int && !is_hw) { + switch(intno) { + case 8: + case 10: + case 11: + case 12: + case 13: + case 14: + case 17: + has_error_code = 1; + break; + } + } + if (is_int) + old_eip = next_eip; + else + old_eip = env->eip; + + dt = &env->idt; + if (intno * 16 + 15 > dt->limit) + raise_exception_err(EXCP0D_GPF, intno * 16 + 2); + ptr = dt->base + intno * 16; + e1 = ldl_kernel(ptr); + e2 = ldl_kernel(ptr + 4); + e3 = ldl_kernel(ptr + 8); + /* check gate type */ + type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; + switch(type) { + case 14: /* 386 interrupt gate */ + case 15: /* 386 trap gate */ + break; + default: + raise_exception_err(EXCP0D_GPF, intno * 16 + 2); + break; + } + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + /* check privledge if software int */ + if (is_int && dpl < cpl) + raise_exception_err(EXCP0D_GPF, intno * 16 + 2); + /* check valid bit */ + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, intno * 16 + 2); + selector = e1 >> 16; + offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff); + ist = e2 & 7; + if ((selector & 0xfffc) == 0) + raise_exception_err(EXCP0D_GPF, 0); + + if (load_segment(&e1, &e2, selector) != 0) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (dpl > cpl) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc); + if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK)) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + if ((!(e2 & DESC_C_MASK) && dpl < cpl) || ist != 0) { + /* to inner priviledge */ + if (ist != 0) + esp = get_rsp_from_tss(ist + 3); + else + esp = get_rsp_from_tss(dpl); + esp &= ~0xfLL; /* align stack */ + ss = 0; + new_stack = 1; + } else if ((e2 & DESC_C_MASK) || dpl == cpl) { + /* to same priviledge */ + if (env->eflags & VM_MASK) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + new_stack = 0; + if (ist != 0) + esp = get_rsp_from_tss(ist + 3); + else + esp = ESP; + esp &= ~0xfLL; /* align stack */ + dpl = cpl; + } else { + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + new_stack = 0; /* avoid warning */ + esp = 0; /* avoid warning */ + } + + PUSHQ(esp, env->segs[R_SS].selector); + PUSHQ(esp, ESP); + PUSHQ(esp, compute_eflags()); + PUSHQ(esp, env->segs[R_CS].selector); + PUSHQ(esp, old_eip); + if (has_error_code) { + PUSHQ(esp, error_code); + } + + if (new_stack) { + ss = 0 | dpl; + cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0); + } + ESP = esp; + + selector = (selector & ~3) | dpl; + cpu_x86_load_seg_cache(env, R_CS, selector, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); + cpu_x86_set_cpl(env, dpl); + env->eip = offset; + + /* interrupt gate clear IF mask */ + if ((type & 1) == 0) { + env->eflags &= ~IF_MASK; + } + env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK); +} +#endif + +void helper_syscall(int next_eip_addend) +{ + int selector; + + if (!(env->efer & MSR_EFER_SCE)) { + raise_exception_err(EXCP06_ILLOP, 0); + } + selector = (env->star >> 32) & 0xffff; +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + int code64; + + ECX = env->eip + next_eip_addend; + env->regs[11] = compute_eflags(); + + code64 = env->hflags & HF_CS64_MASK; + + cpu_x86_set_cpl(env, 0); + cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK); + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_W_MASK | DESC_A_MASK); + env->eflags &= ~env->fmask; + if (code64) + env->eip = env->lstar; + else + env->eip = env->cstar; + } else +#endif + { + ECX = (uint32_t)(env->eip + next_eip_addend); + + cpu_x86_set_cpl(env, 0); + cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_W_MASK | DESC_A_MASK); + env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK); + env->eip = (uint32_t)env->star; + } +} + +void helper_sysret(int dflag) +{ + int cpl, selector; + + if (!(env->efer & MSR_EFER_SCE)) { + raise_exception_err(EXCP06_ILLOP, 0); + } + cpl = env->hflags & HF_CPL_MASK; + if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) { + raise_exception_err(EXCP0D_GPF, 0); + } + selector = (env->star >> 48) & 0xffff; +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + if (dflag == 2) { + cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | + DESC_L_MASK); + env->eip = ECX; + } else { + cpu_x86_load_seg_cache(env, R_CS, selector | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + env->eip = (uint32_t)ECX; + } + cpu_x86_load_seg_cache(env, R_SS, selector + 8, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_W_MASK | DESC_A_MASK); + load_eflags((uint32_t)(env->regs[11]), TF_MASK | AC_MASK | ID_MASK | + IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK); + cpu_x86_set_cpl(env, 3); + } else +#endif + { + cpu_x86_load_seg_cache(env, R_CS, selector | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + env->eip = (uint32_t)ECX; + cpu_x86_load_seg_cache(env, R_SS, selector + 8, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_W_MASK | DESC_A_MASK); + env->eflags |= IF_MASK; + cpu_x86_set_cpl(env, 3); + } +#ifdef USE_KQEMU + if (kqemu_is_ok(env)) { + if (env->hflags & HF_LMA_MASK) + CC_OP = CC_OP_EFLAGS; + env->exception_index = -1; + cpu_loop_exit(); + } +#endif +} + +/* real mode interrupt */ +static void do_interrupt_real(int intno, int is_int, int error_code, + unsigned int next_eip) +{ + SegmentCache *dt; + target_ulong ptr, ssp; + int selector; + uint32_t offset, esp; + uint32_t old_cs, old_eip; + + /* real mode (simpler !) */ + dt = &env->idt; + if (intno * 4 + 3 > dt->limit) + raise_exception_err(EXCP0D_GPF, intno * 8 + 2); + ptr = dt->base + intno * 4; + offset = lduw_kernel(ptr); + selector = lduw_kernel(ptr + 2); + esp = ESP; + ssp = env->segs[R_SS].base; + if (is_int) + old_eip = next_eip; + else + old_eip = env->eip; + old_cs = env->segs[R_CS].selector; + /* XXX: use SS segment size ? */ + PUSHW(ssp, esp, 0xffff, compute_eflags()); + PUSHW(ssp, esp, 0xffff, old_cs); + PUSHW(ssp, esp, 0xffff, old_eip); + + /* update processor state */ + ESP = (ESP & ~0xffff) | (esp & 0xffff); + env->eip = offset; + env->segs[R_CS].selector = selector; + env->segs[R_CS].base = (selector << 4); + env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK); +} + +/* fake user mode interrupt */ +void do_interrupt_user(int intno, int is_int, int error_code, + target_ulong next_eip) +{ + SegmentCache *dt; + target_ulong ptr; + int dpl, cpl; + uint32_t e2; + + dt = &env->idt; + ptr = dt->base + (intno * 8); + e2 = ldl_kernel(ptr + 4); + + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + /* check privledge if software int */ + if (is_int && dpl < cpl) + raise_exception_err(EXCP0D_GPF, intno * 8 + 2); + + /* Since we emulate only user space, we cannot do more than + exiting the emulation with the suitable exception and error + code */ + if (is_int) + EIP = next_eip; +} + +/* + * Begin execution of an interruption. is_int is TRUE if coming from + * the int instruction. next_eip is the EIP value AFTER the interrupt + * instruction. It is only relevant if is_int is TRUE. + */ +void do_interrupt(int intno, int is_int, int error_code, + target_ulong next_eip, int is_hw) +{ + if (loglevel & CPU_LOG_INT) { + if ((env->cr[0] & CR0_PE_MASK)) { + static int count; + fprintf(logfile, "%6d: v=%02x e=%04x i=%d cpl=%d IP=%04x:" TARGET_FMT_lx " pc=" TARGET_FMT_lx " SP=%04x:" TARGET_FMT_lx, + count, intno, error_code, is_int, + env->hflags & HF_CPL_MASK, + env->segs[R_CS].selector, EIP, + (int)env->segs[R_CS].base + EIP, + env->segs[R_SS].selector, ESP); + if (intno == 0x0e) { + fprintf(logfile, " CR2=" TARGET_FMT_lx, env->cr[2]); + } else { + fprintf(logfile, " EAX=" TARGET_FMT_lx, EAX); + } + fprintf(logfile, "\n"); + cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP); +#if 0 + { + int i; + uint8_t *ptr; + fprintf(logfile, " code="); + ptr = env->segs[R_CS].base + env->eip; + for(i = 0; i < 16; i++) { + fprintf(logfile, " %02x", ldub(ptr + i)); + } + fprintf(logfile, "\n"); + } +#endif + count++; + } + } + if (env->cr[0] & CR0_PE_MASK) { +#if TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + do_interrupt64(intno, is_int, error_code, next_eip, is_hw); + } else +#endif + { + do_interrupt_protected(intno, is_int, error_code, next_eip, is_hw); + } + } else { + do_interrupt_real(intno, is_int, error_code, next_eip); + } +} + +/* + * Signal an interruption. It is executed in the main CPU loop. + * is_int is TRUE if coming from the int instruction. next_eip is the + * EIP value AFTER the interrupt instruction. It is only relevant if + * is_int is TRUE. + */ +void raise_interrupt(int intno, int is_int, int error_code, + int next_eip_addend) +{ + env->exception_index = intno; + env->error_code = error_code; + env->exception_is_int = is_int; + env->exception_next_eip = env->eip + next_eip_addend; + cpu_loop_exit(); +} + +/* same as raise_exception_err, but do not restore global registers */ +static void raise_exception_err_norestore(int exception_index, int error_code) +{ + env->exception_index = exception_index; + env->error_code = error_code; + env->exception_is_int = 0; + env->exception_next_eip = 0; + longjmp(env->jmp_env, 1); +} + +/* shortcuts to generate exceptions */ + +void (raise_exception_err)(int exception_index, int error_code) +{ + raise_interrupt(exception_index, 0, error_code, 0); +} + +void raise_exception(int exception_index) +{ + raise_interrupt(exception_index, 0, 0, 0); +} + +#ifdef BUGGY_GCC_DIV64 +/* gcc 2.95.4 on PowerPC does not seem to like using __udivdi3, so we + call it from another function */ +uint32_t div32(uint64_t *q_ptr, uint64_t num, uint32_t den) +{ + *q_ptr = num / den; + return num % den; +} + +int32_t idiv32(int64_t *q_ptr, int64_t num, int32_t den) +{ + *q_ptr = num / den; + return num % den; +} +#endif + +void helper_divl_EAX_T0(void) +{ + unsigned int den, r; + uint64_t num, q; + + num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32); + den = T0; + if (den == 0) { + raise_exception(EXCP00_DIVZ); + } +#ifdef BUGGY_GCC_DIV64 + r = div32(&q, num, den); +#else + q = (num / den); + r = (num % den); +#endif + if (q > 0xffffffff) + raise_exception(EXCP00_DIVZ); + EAX = (uint32_t)q; + EDX = (uint32_t)r; +} + +void helper_idivl_EAX_T0(void) +{ + int den, r; + int64_t num, q; + + num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32); + den = T0; + if (den == 0) { + raise_exception(EXCP00_DIVZ); + } +#ifdef BUGGY_GCC_DIV64 + r = idiv32(&q, num, den); +#else + q = (num / den); + r = (num % den); +#endif + if (q != (int32_t)q) + raise_exception(EXCP00_DIVZ); + EAX = (uint32_t)q; + EDX = (uint32_t)r; +} + +void helper_cmpxchg8b(void) +{ + uint64_t d; + int eflags; + + eflags = cc_table[CC_OP].compute_all(); + d = ldq(A0); + if (d == (((uint64_t)EDX << 32) | EAX)) { + stq(A0, ((uint64_t)ECX << 32) | EBX); + eflags |= CC_Z; + } else { + EDX = d >> 32; + EAX = d; + eflags &= ~CC_Z; + } + CC_SRC = eflags; +} + +void helper_cpuid(void) +{ + uint32_t index; + index = (uint32_t)EAX; + + /* test if maximum index reached */ + if (index & 0x80000000) { + if (index > env->cpuid_xlevel) + index = env->cpuid_level; + } else { + if (index > env->cpuid_level) + index = env->cpuid_level; + } + + switch(index) { + case 0: + EAX = env->cpuid_level; + EBX = env->cpuid_vendor1; + EDX = env->cpuid_vendor2; + ECX = env->cpuid_vendor3; + break; + case 1: + EAX = env->cpuid_version; + EBX = 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ + ECX = env->cpuid_ext_features; + EDX = env->cpuid_features; + break; + case 2: + /* cache info: needed for Pentium Pro compatibility */ + EAX = 0x410601; + EBX = 0; + ECX = 0; + EDX = 0; + break; + case 0x80000000: + EAX = env->cpuid_xlevel; + EBX = env->cpuid_vendor1; + EDX = env->cpuid_vendor2; + ECX = env->cpuid_vendor3; + break; + case 0x80000001: + EAX = env->cpuid_features; + EBX = 0; + ECX = 0; + EDX = env->cpuid_ext2_features; + break; + case 0x80000002: + case 0x80000003: + case 0x80000004: + EAX = env->cpuid_model[(index - 0x80000002) * 4 + 0]; + EBX = env->cpuid_model[(index - 0x80000002) * 4 + 1]; + ECX = env->cpuid_model[(index - 0x80000002) * 4 + 2]; + EDX = env->cpuid_model[(index - 0x80000002) * 4 + 3]; + break; + case 0x80000005: + /* cache info (L1 cache) */ + EAX = 0x01ff01ff; + EBX = 0x01ff01ff; + ECX = 0x40020140; + EDX = 0x40020140; + break; + case 0x80000006: + /* cache info (L2 cache) */ + EAX = 0; + EBX = 0x42004200; + ECX = 0x02008140; + EDX = 0; + break; + case 0x80000008: + /* virtual & phys address size in low 2 bytes. */ + EAX = 0x00003028; + EBX = 0; + ECX = 0; + EDX = 0; + break; + default: + /* reserved values: zero */ + EAX = 0; + EBX = 0; + ECX = 0; + EDX = 0; + break; + } +} + +void helper_enter_level(int level, int data32) +{ + target_ulong ssp; + uint32_t esp_mask, esp, ebp; + + esp_mask = get_sp_mask(env->segs[R_SS].flags); + ssp = env->segs[R_SS].base; + ebp = EBP; + esp = ESP; + if (data32) { + /* 32 bit */ + esp -= 4; + while (--level) { + esp -= 4; + ebp -= 4; + stl(ssp + (esp & esp_mask), ldl(ssp + (ebp & esp_mask))); + } + esp -= 4; + stl(ssp + (esp & esp_mask), T1); + } else { + /* 16 bit */ + esp -= 2; + while (--level) { + esp -= 2; + ebp -= 2; + stw(ssp + (esp & esp_mask), lduw(ssp + (ebp & esp_mask))); + } + esp -= 2; + stw(ssp + (esp & esp_mask), T1); + } +} + +#ifdef TARGET_X86_64 +void helper_enter64_level(int level, int data64) +{ + target_ulong esp, ebp; + ebp = EBP; + esp = ESP; + + if (data64) { + /* 64 bit */ + esp -= 8; + while (--level) { + esp -= 8; + ebp -= 8; + stq(esp, ldq(ebp)); + } + esp -= 8; + stq(esp, T1); + } else { + /* 16 bit */ + esp -= 2; + while (--level) { + esp -= 2; + ebp -= 2; + stw(esp, lduw(ebp)); + } + esp -= 2; + stw(esp, T1); + } +} +#endif + +void helper_lldt_T0(void) +{ + int selector; + SegmentCache *dt; + uint32_t e1, e2; + int index, entry_limit; + target_ulong ptr; + + selector = T0 & 0xffff; + if ((selector & 0xfffc) == 0) { + /* XXX: NULL selector case: invalid LDT */ + env->ldt.base = 0; + env->ldt.limit = 0; + } else { + if (selector & 0x4) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + dt = &env->gdt; + index = selector & ~7; +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) + entry_limit = 15; + else +#endif + entry_limit = 7; + if ((index + entry_limit) > dt->limit) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + ptr = dt->base + index; + e1 = ldl_kernel(ptr); + e2 = ldl_kernel(ptr + 4); + if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc); +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + uint32_t e3; + e3 = ldl_kernel(ptr + 8); + load_seg_cache_raw_dt(&env->ldt, e1, e2); + env->ldt.base |= (target_ulong)e3 << 32; + } else +#endif + { + load_seg_cache_raw_dt(&env->ldt, e1, e2); + } + } + env->ldt.selector = selector; +} + +void helper_ltr_T0(void) +{ + int selector; + SegmentCache *dt; + uint32_t e1, e2; + int index, type, entry_limit; + target_ulong ptr; + + selector = T0 & 0xffff; + if ((selector & 0xfffc) == 0) { + /* NULL selector case: invalid TR */ + env->tr.base = 0; + env->tr.limit = 0; + env->tr.flags = 0; + } else { + if (selector & 0x4) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + dt = &env->gdt; + index = selector & ~7; +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) + entry_limit = 15; + else +#endif + entry_limit = 7; + if ((index + entry_limit) > dt->limit) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + ptr = dt->base + index; + e1 = ldl_kernel(ptr); + e2 = ldl_kernel(ptr + 4); + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; + if ((e2 & DESC_S_MASK) || + (type != 1 && type != 9)) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc); +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + uint32_t e3; + e3 = ldl_kernel(ptr + 8); + load_seg_cache_raw_dt(&env->tr, e1, e2); + env->tr.base |= (target_ulong)e3 << 32; + } else +#endif + { + load_seg_cache_raw_dt(&env->tr, e1, e2); + } + e2 |= DESC_TSS_BUSY_MASK; + stl_kernel(ptr + 4, e2); + } + env->tr.selector = selector; +} + +/* only works if protected mode and not VM86. seg_reg must be != R_CS */ +void load_seg(int seg_reg, int selector) +{ + uint32_t e1, e2; + int cpl, dpl, rpl; + SegmentCache *dt; + int index; + target_ulong ptr; + + selector &= 0xffff; + cpl = env->hflags & HF_CPL_MASK; + if ((selector & 0xfffc) == 0) { + /* null selector case */ + if (seg_reg == R_SS +#ifdef TARGET_X86_64 + && (!(env->hflags & HF_CS64_MASK) || cpl == 3) +#endif + ) + raise_exception_err(EXCP0D_GPF, 0); + cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0); + } else { + + if (selector & 0x4) + dt = &env->ldt; + else + dt = &env->gdt; + index = selector & ~7; + if ((index + 7) > dt->limit) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + ptr = dt->base + index; + e1 = ldl_kernel(ptr); + e2 = ldl_kernel(ptr + 4); + + if (!(e2 & DESC_S_MASK)) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + rpl = selector & 3; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (seg_reg == R_SS) { + /* must be writable segment */ + if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + if (rpl != cpl || dpl != cpl) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + } else { + /* must be readable segment */ + if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + + if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) { + /* if not conforming code, test rights */ + if (dpl < cpl || dpl < rpl) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + } + } + + if (!(e2 & DESC_P_MASK)) { + if (seg_reg == R_SS) + raise_exception_err(EXCP0C_STACK, selector & 0xfffc); + else + raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc); + } + + /* set the access bit if not already set */ + if (!(e2 & DESC_A_MASK)) { + e2 |= DESC_A_MASK; + stl_kernel(ptr + 4, e2); + } + + cpu_x86_load_seg_cache(env, seg_reg, selector, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); +#if 0 + fprintf(logfile, "load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n", + selector, (unsigned long)sc->base, sc->limit, sc->flags); +#endif + } +} + +/* protected mode jump */ +void helper_ljmp_protected_T0_T1(int next_eip_addend) +{ + int new_cs, gate_cs, type; + uint32_t e1, e2, cpl, dpl, rpl, limit; + target_ulong new_eip, next_eip; + + new_cs = T0; + new_eip = T1; + if ((new_cs & 0xfffc) == 0) + raise_exception_err(EXCP0D_GPF, 0); + if (load_segment(&e1, &e2, new_cs) != 0) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + cpl = env->hflags & HF_CPL_MASK; + if (e2 & DESC_S_MASK) { + if (!(e2 & DESC_CS_MASK)) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (e2 & DESC_C_MASK) { + /* conforming code segment */ + if (dpl > cpl) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + } else { + /* non conforming code segment */ + rpl = new_cs & 3; + if (rpl > cpl) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + if (dpl != cpl) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + } + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc); + limit = get_seg_limit(e1, e2); + if (new_eip > limit && + !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK)) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, + get_seg_base(e1, e2), limit, e2); + EIP = new_eip; + } else { + /* jump to call or task gate */ + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + rpl = new_cs & 3; + cpl = env->hflags & HF_CPL_MASK; + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; + switch(type) { + case 1: /* 286 TSS */ + case 9: /* 386 TSS */ + case 5: /* task gate */ + if (dpl < cpl || dpl < rpl) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + next_eip = env->eip + next_eip_addend; + switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP, next_eip); + break; + case 4: /* 286 call gate */ + case 12: /* 386 call gate */ + if ((dpl < cpl) || (dpl < rpl)) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc); + gate_cs = e1 >> 16; + new_eip = (e1 & 0xffff); + if (type == 12) + new_eip |= (e2 & 0xffff0000); + if (load_segment(&e1, &e2, gate_cs) != 0) + raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc); + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + /* must be code segment */ + if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) != + (DESC_S_MASK | DESC_CS_MASK))) + raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc); + if (((e2 & DESC_C_MASK) && (dpl > cpl)) || + (!(e2 & DESC_C_MASK) && (dpl != cpl))) + raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc); + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc); + limit = get_seg_limit(e1, e2); + if (new_eip > limit) + raise_exception_err(EXCP0D_GPF, 0); + cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl, + get_seg_base(e1, e2), limit, e2); + EIP = new_eip; + break; + default: + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + break; + } + } +} + +/* real mode call */ +void helper_lcall_real_T0_T1(int shift, int next_eip) +{ + int new_cs, new_eip; + uint32_t esp, esp_mask; + target_ulong ssp; + + new_cs = T0; + new_eip = T1; + esp = ESP; + esp_mask = get_sp_mask(env->segs[R_SS].flags); + ssp = env->segs[R_SS].base; + if (shift) { + PUSHL(ssp, esp, esp_mask, env->segs[R_CS].selector); + PUSHL(ssp, esp, esp_mask, next_eip); + } else { + PUSHW(ssp, esp, esp_mask, env->segs[R_CS].selector); + PUSHW(ssp, esp, esp_mask, next_eip); + } + + ESP = (ESP & ~esp_mask) | (esp & esp_mask); + env->eip = new_eip; + env->segs[R_CS].selector = new_cs; + env->segs[R_CS].base = (new_cs << 4); +} + +/* protected mode call */ +void helper_lcall_protected_T0_T1(int shift, int next_eip_addend) +{ + int new_cs, new_stack, i; + uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count; + uint32_t ss, ss_e1, ss_e2, sp, type, ss_dpl, sp_mask; + uint32_t val, limit, old_sp_mask; + target_ulong ssp, old_ssp, next_eip, new_eip; + + new_cs = T0; + new_eip = T1; + next_eip = env->eip + next_eip_addend; +#ifdef DEBUG_PCALL + if (loglevel & CPU_LOG_PCALL) { + fprintf(logfile, "lcall %04x:%08x s=%d\n", + new_cs, (uint32_t)new_eip, shift); + cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP); + } +#endif + if ((new_cs & 0xfffc) == 0) + raise_exception_err(EXCP0D_GPF, 0); + if (load_segment(&e1, &e2, new_cs) != 0) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + cpl = env->hflags & HF_CPL_MASK; +#ifdef DEBUG_PCALL + if (loglevel & CPU_LOG_PCALL) { + fprintf(logfile, "desc=%08x:%08x\n", e1, e2); + } +#endif + if (e2 & DESC_S_MASK) { + if (!(e2 & DESC_CS_MASK)) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (e2 & DESC_C_MASK) { + /* conforming code segment */ + if (dpl > cpl) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + } else { + /* non conforming code segment */ + rpl = new_cs & 3; + if (rpl > cpl) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + if (dpl != cpl) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + } + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc); + +#ifdef TARGET_X86_64 + /* XXX: check 16/32 bit cases in long mode */ + if (shift == 2) { + target_ulong rsp; + /* 64 bit case */ + rsp = ESP; + PUSHQ(rsp, env->segs[R_CS].selector); + PUSHQ(rsp, next_eip); + /* from this point, not restartable */ + ESP = rsp; + cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), e2); + EIP = new_eip; + } else +#endif + { + sp = ESP; + sp_mask = get_sp_mask(env->segs[R_SS].flags); + ssp = env->segs[R_SS].base; + if (shift) { + PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector); + PUSHL(ssp, sp, sp_mask, next_eip); + } else { + PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector); + PUSHW(ssp, sp, sp_mask, next_eip); + } + + limit = get_seg_limit(e1, e2); + if (new_eip > limit) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + /* from this point, not restartable */ + ESP = (ESP & ~sp_mask) | (sp & sp_mask); + cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, + get_seg_base(e1, e2), limit, e2); + EIP = new_eip; + } + } else { + /* check gate type */ + type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + rpl = new_cs & 3; + switch(type) { + case 1: /* available 286 TSS */ + case 9: /* available 386 TSS */ + case 5: /* task gate */ + if (dpl < cpl || dpl < rpl) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL, next_eip); + return; + case 4: /* 286 call gate */ + case 12: /* 386 call gate */ + break; + default: + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + break; + } + shift = type >> 3; + + if (dpl < cpl || dpl < rpl) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + /* check valid bit */ + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc); + selector = e1 >> 16; + offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); + param_count = e2 & 0x1f; + if ((selector & 0xfffc) == 0) + raise_exception_err(EXCP0D_GPF, 0); + + if (load_segment(&e1, &e2, selector) != 0) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (dpl > cpl) + raise_exception_err(EXCP0D_GPF, selector & 0xfffc); + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc); + + if (!(e2 & DESC_C_MASK) && dpl < cpl) { + /* to inner priviledge */ + get_ss_esp_from_tss(&ss, &sp, dpl); +#ifdef DEBUG_PCALL + if (loglevel & CPU_LOG_PCALL) + fprintf(logfile, "new ss:esp=%04x:%08x param_count=%d ESP=" TARGET_FMT_lx "\n", + ss, sp, param_count, ESP); +#endif + if ((ss & 0xfffc) == 0) + raise_exception_err(EXCP0A_TSS, ss & 0xfffc); + if ((ss & 3) != dpl) + raise_exception_err(EXCP0A_TSS, ss & 0xfffc); + if (load_segment(&ss_e1, &ss_e2, ss) != 0) + raise_exception_err(EXCP0A_TSS, ss & 0xfffc); + ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; + if (ss_dpl != dpl) + raise_exception_err(EXCP0A_TSS, ss & 0xfffc); + if (!(ss_e2 & DESC_S_MASK) || + (ss_e2 & DESC_CS_MASK) || + !(ss_e2 & DESC_W_MASK)) + raise_exception_err(EXCP0A_TSS, ss & 0xfffc); + if (!(ss_e2 & DESC_P_MASK)) + raise_exception_err(EXCP0A_TSS, ss & 0xfffc); + + // push_size = ((param_count * 2) + 8) << shift; + + old_sp_mask = get_sp_mask(env->segs[R_SS].flags); + old_ssp = env->segs[R_SS].base; + + sp_mask = get_sp_mask(ss_e2); + ssp = get_seg_base(ss_e1, ss_e2); + if (shift) { + PUSHL(ssp, sp, sp_mask, env->segs[R_SS].selector); + PUSHL(ssp, sp, sp_mask, ESP); + for(i = param_count - 1; i >= 0; i--) { + val = ldl_kernel(old_ssp + ((ESP + i * 4) & old_sp_mask)); + PUSHL(ssp, sp, sp_mask, val); + } + } else { + PUSHW(ssp, sp, sp_mask, env->segs[R_SS].selector); + PUSHW(ssp, sp, sp_mask, ESP); + for(i = param_count - 1; i >= 0; i--) { + val = lduw_kernel(old_ssp + ((ESP + i * 2) & old_sp_mask)); + PUSHW(ssp, sp, sp_mask, val); + } + } + new_stack = 1; + } else { + /* to same priviledge */ + sp = ESP; + sp_mask = get_sp_mask(env->segs[R_SS].flags); + ssp = env->segs[R_SS].base; + // push_size = (4 << shift); + new_stack = 0; + } + + if (shift) { + PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector); + PUSHL(ssp, sp, sp_mask, next_eip); + } else { + PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector); + PUSHW(ssp, sp, sp_mask, next_eip); + } + + /* from this point, not restartable */ + + if (new_stack) { + ss = (ss & ~3) | dpl; + cpu_x86_load_seg_cache(env, R_SS, ss, + ssp, + get_seg_limit(ss_e1, ss_e2), + ss_e2); + } + + selector = (selector & ~3) | dpl; + cpu_x86_load_seg_cache(env, R_CS, selector, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); + cpu_x86_set_cpl(env, dpl); + ESP = (ESP & ~sp_mask) | (sp & sp_mask); + EIP = offset; + } +#ifdef USE_KQEMU + if (kqemu_is_ok(env)) { + env->exception_index = -1; + cpu_loop_exit(); + } +#endif +} + +/* real and vm86 mode iret */ +void helper_iret_real(int shift) +{ + uint32_t sp, new_cs, new_eip, new_eflags, sp_mask; + target_ulong ssp; + int eflags_mask; + + sp_mask = 0xffff; /* XXXX: use SS segment size ? */ + sp = ESP; + ssp = env->segs[R_SS].base; + if (shift == 1) { + /* 32 bits */ + POPL(ssp, sp, sp_mask, new_eip); + POPL(ssp, sp, sp_mask, new_cs); + new_cs &= 0xffff; + POPL(ssp, sp, sp_mask, new_eflags); + } else { + /* 16 bits */ + POPW(ssp, sp, sp_mask, new_eip); + POPW(ssp, sp, sp_mask, new_cs); + POPW(ssp, sp, sp_mask, new_eflags); + } + ESP = (ESP & ~sp_mask) | (sp & sp_mask); + load_seg_vm(R_CS, new_cs); + env->eip = new_eip; + if (env->eflags & VM_MASK) + eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK | NT_MASK; + else + eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | RF_MASK | NT_MASK; + if (shift == 0) + eflags_mask &= 0xffff; + load_eflags(new_eflags, eflags_mask); +} + +static inline void validate_seg(int seg_reg, int cpl) +{ + int dpl; + uint32_t e2; + + /* XXX: on x86_64, we do not want to nullify FS and GS because + they may still contain a valid base. I would be interested to + know how a real x86_64 CPU behaves */ + if ((seg_reg == R_FS || seg_reg == R_GS) && + (env->segs[seg_reg].selector & 0xfffc) == 0) + return; + + e2 = env->segs[seg_reg].flags; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) { + /* data or non conforming code segment */ + if (dpl < cpl) { + cpu_x86_load_seg_cache(env, seg_reg, 0, 0, 0, 0); + } + } +} + +/* protected mode iret */ +static inline void helper_ret_protected(int shift, int is_iret, int addend) +{ + uint32_t new_cs, new_eflags, new_ss; + uint32_t new_es, new_ds, new_fs, new_gs; + uint32_t e1, e2, ss_e1, ss_e2; + int cpl, dpl, rpl, eflags_mask, iopl; + target_ulong ssp, sp, new_eip, new_esp, sp_mask; + +#ifdef TARGET_X86_64 + if (shift == 2) + sp_mask = -1; + else +#endif + sp_mask = get_sp_mask(env->segs[R_SS].flags); + sp = ESP; + ssp = env->segs[R_SS].base; + new_eflags = 0; /* avoid warning */ +#ifdef TARGET_X86_64 + if (shift == 2) { + POPQ(sp, new_eip); + POPQ(sp, new_cs); + new_cs &= 0xffff; + if (is_iret) { + POPQ(sp, new_eflags); + } + } else +#endif + if (shift == 1) { + /* 32 bits */ + POPL(ssp, sp, sp_mask, new_eip); + POPL(ssp, sp, sp_mask, new_cs); + new_cs &= 0xffff; + if (is_iret) { + POPL(ssp, sp, sp_mask, new_eflags); + if (new_eflags & VM_MASK) + goto return_to_vm86; + } + } else { + /* 16 bits */ + POPW(ssp, sp, sp_mask, new_eip); + POPW(ssp, sp, sp_mask, new_cs); + if (is_iret) + POPW(ssp, sp, sp_mask, new_eflags); + } +#ifdef DEBUG_PCALL + if (loglevel & CPU_LOG_PCALL) { + fprintf(logfile, "lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n", + new_cs, new_eip, shift, addend); + cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP); + } +#endif + if ((new_cs & 0xfffc) == 0) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + if (load_segment(&e1, &e2, new_cs) != 0) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + if (!(e2 & DESC_S_MASK) || + !(e2 & DESC_CS_MASK)) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + cpl = env->hflags & HF_CPL_MASK; + rpl = new_cs & 3; + if (rpl < cpl) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (e2 & DESC_C_MASK) { + if (dpl > rpl) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + } else { + if (dpl != rpl) + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); + } + if (!(e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc); + + sp += addend; + if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) || + ((env->hflags & HF_CS64_MASK) && !is_iret))) { + /* return to same priledge level */ + cpu_x86_load_seg_cache(env, R_CS, new_cs, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); + } else { + /* return to different priviledge level */ +#ifdef TARGET_X86_64 + if (shift == 2) { + POPQ(sp, new_esp); + POPQ(sp, new_ss); + new_ss &= 0xffff; + } else +#endif + if (shift == 1) { + /* 32 bits */ + POPL(ssp, sp, sp_mask, new_esp); + POPL(ssp, sp, sp_mask, new_ss); + new_ss &= 0xffff; + } else { + /* 16 bits */ + POPW(ssp, sp, sp_mask, new_esp); + POPW(ssp, sp, sp_mask, new_ss); + } +#ifdef DEBUG_PCALL + if (loglevel & CPU_LOG_PCALL) { + fprintf(logfile, "new ss:esp=%04x:" TARGET_FMT_lx "\n", + new_ss, new_esp); + } +#endif + if ((new_ss & 0xfffc) == 0) { +#ifdef TARGET_X86_64 + /* NULL ss is allowed in long mode if cpl != 3*/ + if ((env->hflags & HF_LMA_MASK) && rpl != 3) { + cpu_x86_load_seg_cache(env, R_SS, new_ss, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (rpl << DESC_DPL_SHIFT) | + DESC_W_MASK | DESC_A_MASK); + } else +#endif + { + raise_exception_err(EXCP0D_GPF, 0); + } + } else { + if ((new_ss & 3) != rpl) + raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc); + if (load_segment(&ss_e1, &ss_e2, new_ss) != 0) + raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc); + if (!(ss_e2 & DESC_S_MASK) || + (ss_e2 & DESC_CS_MASK) || + !(ss_e2 & DESC_W_MASK)) + raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc); + dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; + if (dpl != rpl) + raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc); + if (!(ss_e2 & DESC_P_MASK)) + raise_exception_err(EXCP0B_NOSEG, new_ss & 0xfffc); + cpu_x86_load_seg_cache(env, R_SS, new_ss, + get_seg_base(ss_e1, ss_e2), + get_seg_limit(ss_e1, ss_e2), + ss_e2); + } + + cpu_x86_load_seg_cache(env, R_CS, new_cs, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); + cpu_x86_set_cpl(env, rpl); + sp = new_esp; +#ifdef TARGET_X86_64 + if (env->hflags & HF_CS64_MASK) + sp_mask = -1; + else +#endif + sp_mask = get_sp_mask(ss_e2); + + /* validate data segments */ + validate_seg(R_ES, rpl); + validate_seg(R_DS, rpl); + validate_seg(R_FS, rpl); + validate_seg(R_GS, rpl); + + sp += addend; + } + ESP = (ESP & ~sp_mask) | (sp & sp_mask); + env->eip = new_eip; + if (is_iret) { + /* NOTE: 'cpl' is the _old_ CPL */ + eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK; + if (cpl == 0) + eflags_mask |= IOPL_MASK; + iopl = (env->eflags >> IOPL_SHIFT) & 3; + if (cpl <= iopl) + eflags_mask |= IF_MASK; + if (shift == 0) + eflags_mask &= 0xffff; + load_eflags(new_eflags, eflags_mask); + } + return; + + return_to_vm86: + POPL(ssp, sp, sp_mask, new_esp); + POPL(ssp, sp, sp_mask, new_ss); + POPL(ssp, sp, sp_mask, new_es); + POPL(ssp, sp, sp_mask, new_ds); + POPL(ssp, sp, sp_mask, new_fs); + POPL(ssp, sp, sp_mask, new_gs); + + /* modify processor state */ + load_eflags(new_eflags, TF_MASK | AC_MASK | ID_MASK | + IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK | VIP_MASK); + load_seg_vm(R_CS, new_cs & 0xffff); + cpu_x86_set_cpl(env, 3); + load_seg_vm(R_SS, new_ss & 0xffff); + load_seg_vm(R_ES, new_es & 0xffff); + load_seg_vm(R_DS, new_ds & 0xffff); + load_seg_vm(R_FS, new_fs & 0xffff); + load_seg_vm(R_GS, new_gs & 0xffff); + + env->eip = new_eip & 0xffff; + ESP = new_esp; +} + +void helper_iret_protected(int shift, int next_eip) +{ + int tss_selector, type; + uint32_t e1, e2; + + /* specific case for TSS */ + if (env->eflags & NT_MASK) { +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) + raise_exception_err(EXCP0D_GPF, 0); +#endif + tss_selector = lduw_kernel(env->tr.base + 0); + if (tss_selector & 4) + raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc); + if (load_segment(&e1, &e2, tss_selector) != 0) + raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc); + type = (e2 >> DESC_TYPE_SHIFT) & 0x17; + /* NOTE: we check both segment and busy TSS */ + if (type != 3) + raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc); + switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip); + } else { + helper_ret_protected(shift, 1, 0); + } +#ifdef USE_KQEMU + if (kqemu_is_ok(env)) { + CC_OP = CC_OP_EFLAGS; + env->exception_index = -1; + cpu_loop_exit(); + } +#endif +} + +void helper_lret_protected(int shift, int addend) +{ + helper_ret_protected(shift, 0, addend); +#ifdef USE_KQEMU + if (kqemu_is_ok(env)) { + env->exception_index = -1; + cpu_loop_exit(); + } +#endif +} + +void helper_sysenter(void) +{ + if (env->sysenter_cs == 0) { + raise_exception_err(EXCP0D_GPF, 0); + } + env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK); + cpu_x86_set_cpl(env, 0); + cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_W_MASK | DESC_A_MASK); + ESP = env->sysenter_esp; + EIP = env->sysenter_eip; +} + +void helper_sysexit(void) +{ + int cpl; + + cpl = env->hflags & HF_CPL_MASK; + if (env->sysenter_cs == 0 || cpl != 0) { + raise_exception_err(EXCP0D_GPF, 0); + } + cpu_x86_set_cpl(env, 3); + cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_W_MASK | DESC_A_MASK); + ESP = ECX; + EIP = EDX; +#ifdef USE_KQEMU + if (kqemu_is_ok(env)) { + env->exception_index = -1; + cpu_loop_exit(); + } +#endif +} + +void helper_movl_crN_T0(int reg) +{ +#if !defined(CONFIG_USER_ONLY) + switch(reg) { + case 0: + cpu_x86_update_cr0(env, T0); + break; + case 3: + cpu_x86_update_cr3(env, T0); + break; + case 4: + cpu_x86_update_cr4(env, T0); + break; + case 8: + cpu_set_apic_tpr(env, T0); + break; + default: + env->cr[reg] = T0; + break; + } +#endif +} + +/* XXX: do more */ +void helper_movl_drN_T0(int reg) +{ + env->dr[reg] = T0; +} + +void helper_invlpg(target_ulong addr) +{ + cpu_x86_flush_tlb(env, addr); +} + +void helper_rdtsc(void) +{ + uint64_t val; + + if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { + raise_exception(EXCP0D_GPF); + } + val = cpu_get_tsc(env); + EAX = (uint32_t)(val); + EDX = (uint32_t)(val >> 32); +} + +#if defined(CONFIG_USER_ONLY) +void helper_wrmsr(void) +{ +} + +void helper_rdmsr(void) +{ +} +#else +void helper_wrmsr(void) +{ + uint64_t val; + + val = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32); + + switch((uint32_t)ECX) { + case MSR_IA32_SYSENTER_CS: + env->sysenter_cs = val & 0xffff; + break; + case MSR_IA32_SYSENTER_ESP: + env->sysenter_esp = val; + break; + case MSR_IA32_SYSENTER_EIP: + env->sysenter_eip = val; + break; + case MSR_IA32_APICBASE: + cpu_set_apic_base(env, val); + break; + case MSR_EFER: + { + uint64_t update_mask; + update_mask = 0; + if (env->cpuid_ext2_features & CPUID_EXT2_SYSCALL) + update_mask |= MSR_EFER_SCE; + if (env->cpuid_ext2_features & CPUID_EXT2_LM) + update_mask |= MSR_EFER_LME; + if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR) + update_mask |= MSR_EFER_FFXSR; + if (env->cpuid_ext2_features & CPUID_EXT2_NX) + update_mask |= MSR_EFER_NXE; + env->efer = (env->efer & ~update_mask) | + (val & update_mask); + } + break; + case MSR_STAR: + env->star = val; + break; + case MSR_PAT: + env->pat = val; + break; +#ifdef TARGET_X86_64 + case MSR_LSTAR: + env->lstar = val; + break; + case MSR_CSTAR: + env->cstar = val; + break; + case MSR_FMASK: + env->fmask = val; + break; + case MSR_FSBASE: + env->segs[R_FS].base = val; + break; + case MSR_GSBASE: + env->segs[R_GS].base = val; + break; + case MSR_KERNELGSBASE: + env->kernelgsbase = val; + break; +#endif + default: + /* XXX: exception ? */ + break; + } +} + +void helper_rdmsr(void) +{ + uint64_t val; + switch((uint32_t)ECX) { + case MSR_IA32_SYSENTER_CS: + val = env->sysenter_cs; + break; + case MSR_IA32_SYSENTER_ESP: + val = env->sysenter_esp; + break; + case MSR_IA32_SYSENTER_EIP: + val = env->sysenter_eip; + break; + case MSR_IA32_APICBASE: + val = cpu_get_apic_base(env); + break; + case MSR_EFER: + val = env->efer; + break; + case MSR_STAR: + val = env->star; + break; + case MSR_PAT: + val = env->pat; + break; +#ifdef TARGET_X86_64 + case MSR_LSTAR: + val = env->lstar; + break; + case MSR_CSTAR: + val = env->cstar; + break; + case MSR_FMASK: + val = env->fmask; + break; + case MSR_FSBASE: + val = env->segs[R_FS].base; + break; + case MSR_GSBASE: + val = env->segs[R_GS].base; + break; + case MSR_KERNELGSBASE: + val = env->kernelgsbase; + break; +#endif + default: + /* XXX: exception ? */ + val = 0; + break; + } + EAX = (uint32_t)(val); + EDX = (uint32_t)(val >> 32); +} +#endif + +void helper_lsl(void) +{ + unsigned int selector, limit; + uint32_t e1, e2, eflags; + int rpl, dpl, cpl, type; + + eflags = cc_table[CC_OP].compute_all(); + selector = T0 & 0xffff; + if (load_segment(&e1, &e2, selector) != 0) + goto fail; + rpl = selector & 3; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + if (e2 & DESC_S_MASK) { + if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) { + /* conforming */ + } else { + if (dpl < cpl || dpl < rpl) + goto fail; + } + } else { + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; + switch(type) { + case 1: + case 2: + case 3: + case 9: + case 11: + break; + default: + goto fail; + } + if (dpl < cpl || dpl < rpl) { + fail: + CC_SRC = eflags & ~CC_Z; + return; + } + } + limit = get_seg_limit(e1, e2); + T1 = limit; + CC_SRC = eflags | CC_Z; +} + +void helper_lar(void) +{ + unsigned int selector; + uint32_t e1, e2, eflags; + int rpl, dpl, cpl, type; + + eflags = cc_table[CC_OP].compute_all(); + selector = T0 & 0xffff; + if ((selector & 0xfffc) == 0) + goto fail; + if (load_segment(&e1, &e2, selector) != 0) + goto fail; + rpl = selector & 3; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + if (e2 & DESC_S_MASK) { + if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) { + /* conforming */ + } else { + if (dpl < cpl || dpl < rpl) + goto fail; + } + } else { + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; + switch(type) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 9: + case 11: + case 12: + break; + default: + goto fail; + } + if (dpl < cpl || dpl < rpl) { + fail: + CC_SRC = eflags & ~CC_Z; + return; + } + } + T1 = e2 & 0x00f0ff00; + CC_SRC = eflags | CC_Z; +} + +void helper_verr(void) +{ + unsigned int selector; + uint32_t e1, e2, eflags; + int rpl, dpl, cpl; + + eflags = cc_table[CC_OP].compute_all(); + selector = T0 & 0xffff; + if ((selector & 0xfffc) == 0) + goto fail; + if (load_segment(&e1, &e2, selector) != 0) + goto fail; + if (!(e2 & DESC_S_MASK)) + goto fail; + rpl = selector & 3; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + if (e2 & DESC_CS_MASK) { + if (!(e2 & DESC_R_MASK)) + goto fail; + if (!(e2 & DESC_C_MASK)) { + if (dpl < cpl || dpl < rpl) + goto fail; + } + } else { + if (dpl < cpl || dpl < rpl) { + fail: + CC_SRC = eflags & ~CC_Z; + return; + } + } + CC_SRC = eflags | CC_Z; +} + +void helper_verw(void) +{ + unsigned int selector; + uint32_t e1, e2, eflags; + int rpl, dpl, cpl; + + eflags = cc_table[CC_OP].compute_all(); + selector = T0 & 0xffff; + if ((selector & 0xfffc) == 0) + goto fail; + if (load_segment(&e1, &e2, selector) != 0) + goto fail; + if (!(e2 & DESC_S_MASK)) + goto fail; + rpl = selector & 3; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + if (e2 & DESC_CS_MASK) { + goto fail; + } else { + if (dpl < cpl || dpl < rpl) + goto fail; + if (!(e2 & DESC_W_MASK)) { + fail: + CC_SRC = eflags & ~CC_Z; + return; + } + } + CC_SRC = eflags | CC_Z; +} + +/* FPU helpers */ + +void helper_fldt_ST0_A0(void) +{ + int new_fpstt; + new_fpstt = (env->fpstt - 1) & 7; + env->fpregs[new_fpstt].d = helper_fldt(A0); + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void helper_fstt_ST0_A0(void) +{ + helper_fstt(ST0, A0); +} + +void fpu_set_exception(int mask) +{ + env->fpus |= mask; + if (env->fpus & (~env->fpuc & FPUC_EM)) + env->fpus |= FPUS_SE | FPUS_B; +} + +CPU86_LDouble helper_fdiv(CPU86_LDouble a, CPU86_LDouble b) +{ + if (b == 0.0) + fpu_set_exception(FPUS_ZE); + return a / b; +} + +void fpu_raise_exception(void) +{ + if (env->cr[0] & CR0_NE_MASK) { + raise_exception(EXCP10_COPR); + } +#if !defined(CONFIG_USER_ONLY) + else { + cpu_set_ferr(env); + } +#endif +} + +/* BCD ops */ + +void helper_fbld_ST0_A0(void) +{ + CPU86_LDouble tmp; + uint64_t val; + unsigned int v; + int i; + + val = 0; + for(i = 8; i >= 0; i--) { + v = ldub(A0 + i); + val = (val * 100) + ((v >> 4) * 10) + (v & 0xf); + } + tmp = val; + if (ldub(A0 + 9) & 0x80) + tmp = -tmp; + fpush(); + ST0 = tmp; +} + +void helper_fbst_ST0_A0(void) +{ + int v; + target_ulong mem_ref, mem_end; + int64_t val; + + val = floatx_to_int64(ST0, &env->fp_status); + mem_ref = A0; + mem_end = mem_ref + 9; + if (val < 0) { + stb(mem_end, 0x80); + val = -val; + } else { + stb(mem_end, 0x00); + } + while (mem_ref < mem_end) { + if (val == 0) + break; + v = val % 100; + val = val / 100; + v = ((v / 10) << 4) | (v % 10); + stb(mem_ref++, v); + } + while (mem_ref < mem_end) { + stb(mem_ref++, 0); + } +} + +void helper_f2xm1(void) +{ + ST0 = pow(2.0,ST0) - 1.0; +} + +void helper_fyl2x(void) +{ + CPU86_LDouble fptemp; + + fptemp = ST0; + if (fptemp>0.0){ + fptemp = log(fptemp)/log(2.0); /* log2(ST) */ + ST1 *= fptemp; + fpop(); + } else { + env->fpus &= (~0x4700); + env->fpus |= 0x400; + } +} + +void helper_fptan(void) +{ + CPU86_LDouble fptemp; + + fptemp = ST0; + if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) { + env->fpus |= 0x400; + } else { + ST0 = tan(fptemp); + fpush(); + ST0 = 1.0; + env->fpus &= (~0x400); /* C2 <-- 0 */ + /* the above code is for |arg| < 2**52 only */ + } +} + +void helper_fpatan(void) +{ + CPU86_LDouble fptemp, fpsrcop; + + fpsrcop = ST1; + fptemp = ST0; + ST1 = atan2(fpsrcop,fptemp); + fpop(); +} + +void helper_fxtract(void) +{ + CPU86_LDoubleU temp; + unsigned int expdif; + + temp.d = ST0; + expdif = EXPD(temp) - EXPBIAS; + /*DP exponent bias*/ + ST0 = expdif; + fpush(); + BIASEXPONENT(temp); + ST0 = temp.d; +} + +void helper_fprem1(void) +{ + CPU86_LDouble dblq, fpsrcop, fptemp; + CPU86_LDoubleU fpsrcop1, fptemp1; + int expdif; + int q; + + fpsrcop = ST0; + fptemp = ST1; + fpsrcop1.d = fpsrcop; + fptemp1.d = fptemp; + expdif = EXPD(fpsrcop1) - EXPD(fptemp1); + if (expdif < 53) { + dblq = fpsrcop / fptemp; + dblq = (dblq < 0.0)? ceil(dblq): floor(dblq); + ST0 = fpsrcop - fptemp*dblq; + q = (int)dblq; /* cutting off top bits is assumed here */ + env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */ + /* (C0,C1,C3) <-- (q2,q1,q0) */ + env->fpus |= (q&0x4) << 6; /* (C0) <-- q2 */ + env->fpus |= (q&0x2) << 8; /* (C1) <-- q1 */ + env->fpus |= (q&0x1) << 14; /* (C3) <-- q0 */ + } else { + env->fpus |= 0x400; /* C2 <-- 1 */ + fptemp = pow(2.0, expdif-50); + fpsrcop = (ST0 / ST1) / fptemp; + /* fpsrcop = integer obtained by rounding to the nearest */ + fpsrcop = (fpsrcop-floor(fpsrcop) < ceil(fpsrcop)-fpsrcop)? + floor(fpsrcop): ceil(fpsrcop); + ST0 -= (ST1 * fpsrcop * fptemp); + } +} + +void helper_fprem(void) +{ + CPU86_LDouble dblq, fpsrcop, fptemp; + CPU86_LDoubleU fpsrcop1, fptemp1; + int expdif; + int q; + + fpsrcop = ST0; + fptemp = ST1; + fpsrcop1.d = fpsrcop; + fptemp1.d = fptemp; + expdif = EXPD(fpsrcop1) - EXPD(fptemp1); + if ( expdif < 53 ) { + dblq = fpsrcop / fptemp; + dblq = (dblq < 0.0)? ceil(dblq): floor(dblq); + ST0 = fpsrcop - fptemp*dblq; + q = (int)dblq; /* cutting off top bits is assumed here */ + env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */ + /* (C0,C1,C3) <-- (q2,q1,q0) */ + env->fpus |= (q&0x4) << 6; /* (C0) <-- q2 */ + env->fpus |= (q&0x2) << 8; /* (C1) <-- q1 */ + env->fpus |= (q&0x1) << 14; /* (C3) <-- q0 */ + } else { + env->fpus |= 0x400; /* C2 <-- 1 */ + fptemp = pow(2.0, expdif-50); + fpsrcop = (ST0 / ST1) / fptemp; + /* fpsrcop = integer obtained by chopping */ + fpsrcop = (fpsrcop < 0.0)? + -(floor(fabs(fpsrcop))): floor(fpsrcop); + ST0 -= (ST1 * fpsrcop * fptemp); + } +} + +void helper_fyl2xp1(void) +{ + CPU86_LDouble fptemp; + + fptemp = ST0; + if ((fptemp+1.0)>0.0) { + fptemp = log(fptemp+1.0) / log(2.0); /* log2(ST+1.0) */ + ST1 *= fptemp; + fpop(); + } else { + env->fpus &= (~0x4700); + env->fpus |= 0x400; + } +} + +void helper_fsqrt(void) +{ + CPU86_LDouble fptemp; + + fptemp = ST0; + if (fptemp<0.0) { + env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */ + env->fpus |= 0x400; + } + ST0 = sqrt(fptemp); +} + +void helper_fsincos(void) +{ + CPU86_LDouble fptemp; + + fptemp = ST0; + if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) { + env->fpus |= 0x400; + } else { + ST0 = sin(fptemp); + fpush(); + ST0 = cos(fptemp); + env->fpus &= (~0x400); /* C2 <-- 0 */ + /* the above code is for |arg| < 2**63 only */ + } +} + +void helper_frndint(void) +{ + ST0 = floatx_round_to_int(ST0, &env->fp_status); +} + +void helper_fscale(void) +{ + ST0 = ldexp (ST0, (int)(ST1)); +} + +void helper_fsin(void) +{ + CPU86_LDouble fptemp; + + fptemp = ST0; + if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) { + env->fpus |= 0x400; + } else { + ST0 = sin(fptemp); + env->fpus &= (~0x400); /* C2 <-- 0 */ + /* the above code is for |arg| < 2**53 only */ + } +} + +void helper_fcos(void) +{ + CPU86_LDouble fptemp; + + fptemp = ST0; + if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) { + env->fpus |= 0x400; + } else { + ST0 = cos(fptemp); + env->fpus &= (~0x400); /* C2 <-- 0 */ + /* the above code is for |arg5 < 2**63 only */ + } +} + +void helper_fxam_ST0(void) +{ + CPU86_LDoubleU temp; + int expdif; + + temp.d = ST0; + + env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */ + if (SIGND(temp)) + env->fpus |= 0x200; /* C1 <-- 1 */ + + expdif = EXPD(temp); + if (expdif == MAXEXPD) { + if (MANTD(temp) == 0) + env->fpus |= 0x500 /*Infinity*/; + else + env->fpus |= 0x100 /*NaN*/; + } else if (expdif == 0) { + if (MANTD(temp) == 0) + env->fpus |= 0x4000 /*Zero*/; + else + env->fpus |= 0x4400 /*Denormal*/; + } else { + env->fpus |= 0x400; + } +} + +void helper_fstenv(target_ulong ptr, int data32) +{ + int fpus, fptag, exp, i; + uint64_t mant; + CPU86_LDoubleU tmp; + + fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + fptag = 0; + for (i=7; i>=0; i--) { + fptag <<= 2; + if (env->fptags[i]) { + fptag |= 3; + } else { + tmp.d = env->fpregs[i].d; + exp = EXPD(tmp); + mant = MANTD(tmp); + if (exp == 0 && mant == 0) { + /* zero */ + fptag |= 1; + } else if (exp == 0 || exp == MAXEXPD +#ifdef USE_X86LDOUBLE + || (mant & (1LL << 63)) == 0 +#endif + ) { + /* NaNs, infinity, denormal */ + fptag |= 2; + } + } + } + if (data32) { + /* 32 bit */ + stl(ptr, env->fpuc); + stl(ptr + 4, fpus); + stl(ptr + 8, fptag); + stl(ptr + 12, 0); /* fpip */ + stl(ptr + 16, 0); /* fpcs */ + stl(ptr + 20, 0); /* fpoo */ + stl(ptr + 24, 0); /* fpos */ + } else { + /* 16 bit */ + stw(ptr, env->fpuc); + stw(ptr + 2, fpus); + stw(ptr + 4, fptag); + stw(ptr + 6, 0); + stw(ptr + 8, 0); + stw(ptr + 10, 0); + stw(ptr + 12, 0); + } +} + +void helper_fldenv(target_ulong ptr, int data32) +{ + int i, fpus, fptag; + + if (data32) { + env->fpuc = lduw(ptr); + fpus = lduw(ptr + 4); + fptag = lduw(ptr + 8); + } + else { + env->fpuc = lduw(ptr); + fpus = lduw(ptr + 2); + fptag = lduw(ptr + 4); + } + env->fpstt = (fpus >> 11) & 7; + env->fpus = fpus & ~0x3800; + for(i = 0;i < 8; i++) { + env->fptags[i] = ((fptag & 3) == 3); + fptag >>= 2; + } +} + +void helper_fsave(target_ulong ptr, int data32) +{ + CPU86_LDouble tmp; + int i; + + helper_fstenv(ptr, data32); + + ptr += (14 << data32); + for(i = 0;i < 8; i++) { + tmp = ST(i); + helper_fstt(tmp, ptr); + ptr += 10; + } + + /* fninit */ + env->fpus = 0; + env->fpstt = 0; + env->fpuc = 0x37f; + env->fptags[0] = 1; + env->fptags[1] = 1; + env->fptags[2] = 1; + env->fptags[3] = 1; + env->fptags[4] = 1; + env->fptags[5] = 1; + env->fptags[6] = 1; + env->fptags[7] = 1; +} + +void helper_frstor(target_ulong ptr, int data32) +{ + CPU86_LDouble tmp; + int i; + + helper_fldenv(ptr, data32); + ptr += (14 << data32); + + for(i = 0;i < 8; i++) { + tmp = helper_fldt(ptr); + ST(i) = tmp; + ptr += 10; + } +} + +void helper_fxsave(target_ulong ptr, int data64) +{ + int fpus, fptag, i, nb_xmm_regs; + CPU86_LDouble tmp; + target_ulong addr; + + fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + fptag = 0; + for(i = 0; i < 8; i++) { + fptag |= (env->fptags[i] << i); + } + stw(ptr, env->fpuc); + stw(ptr + 2, fpus); + stw(ptr + 4, fptag ^ 0xff); + + addr = ptr + 0x20; + for(i = 0;i < 8; i++) { + tmp = ST(i); + helper_fstt(tmp, addr); + addr += 16; + } + + if (env->cr[4] & CR4_OSFXSR_MASK) { + /* XXX: finish it */ + stl(ptr + 0x18, env->mxcsr); /* mxcsr */ + stl(ptr + 0x1c, 0x0000ffff); /* mxcsr_mask */ + nb_xmm_regs = 8 << data64; + addr = ptr + 0xa0; + for(i = 0; i < nb_xmm_regs; i++) { + stq(addr, env->xmm_regs[i].XMM_Q(0)); + stq(addr + 8, env->xmm_regs[i].XMM_Q(1)); + addr += 16; + } + } +} + +void helper_fxrstor(target_ulong ptr, int data64) +{ + int i, fpus, fptag, nb_xmm_regs; + CPU86_LDouble tmp; + target_ulong addr; + + env->fpuc = lduw(ptr); + fpus = lduw(ptr + 2); + fptag = lduw(ptr + 4); + env->fpstt = (fpus >> 11) & 7; + env->fpus = fpus & ~0x3800; + fptag ^= 0xff; + for(i = 0;i < 8; i++) { + env->fptags[i] = ((fptag >> i) & 1); + } + + addr = ptr + 0x20; + for(i = 0;i < 8; i++) { + tmp = helper_fldt(addr); + ST(i) = tmp; + addr += 16; + } + + if (env->cr[4] & CR4_OSFXSR_MASK) { + /* XXX: finish it */ + env->mxcsr = ldl(ptr + 0x18); + //ldl(ptr + 0x1c); + nb_xmm_regs = 8 << data64; + addr = ptr + 0xa0; + for(i = 0; i < nb_xmm_regs; i++) { + env->xmm_regs[i].XMM_Q(0) = ldq(addr); + env->xmm_regs[i].XMM_Q(1) = ldq(addr + 8); + addr += 16; + } + } +} + +#ifndef USE_X86LDOUBLE + +void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f) +{ + CPU86_LDoubleU temp; + int e; + + temp.d = f; + /* mantissa */ + *pmant = (MANTD(temp) << 11) | (1LL << 63); + /* exponent + sign */ + e = EXPD(temp) - EXPBIAS + 16383; + e |= SIGND(temp) >> 16; + *pexp = e; +} + +CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper) +{ + CPU86_LDoubleU temp; + int e; + uint64_t ll; + + /* XXX: handle overflow ? */ + e = (upper & 0x7fff) - 16383 + EXPBIAS; /* exponent */ + e |= (upper >> 4) & 0x800; /* sign */ + ll = (mant >> 11) & ((1LL << 52) - 1); +#ifdef __arm__ + temp.l.upper = (e << 20) | (ll >> 32); + temp.l.lower = ll; +#else + temp.ll = ll | ((uint64_t)e << 52); +#endif + return temp.d; +} + +#else + +void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f) +{ + CPU86_LDoubleU temp; + + temp.d = f; + *pmant = temp.l.lower; + *pexp = temp.l.upper; +} + +CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper) +{ + CPU86_LDoubleU temp; + + temp.l.upper = upper; + temp.l.lower = mant; + return temp.d; +} +#endif + +#ifdef TARGET_X86_64 + +//#define DEBUG_MULDIV + +static void add128(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b) +{ + *plow += a; + /* carry test */ + if (*plow < a) + (*phigh)++; + *phigh += b; +} + +static void neg128(uint64_t *plow, uint64_t *phigh) +{ + *plow = ~ *plow; + *phigh = ~ *phigh; + add128(plow, phigh, 1, 0); +} + +static void mul64(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b) +{ + uint32_t a0, a1, b0, b1; + uint64_t v; + + a0 = a; + a1 = a >> 32; + + b0 = b; + b1 = b >> 32; + + v = (uint64_t)a0 * (uint64_t)b0; + *plow = v; + *phigh = 0; + + v = (uint64_t)a0 * (uint64_t)b1; + add128(plow, phigh, v << 32, v >> 32); + + v = (uint64_t)a1 * (uint64_t)b0; + add128(plow, phigh, v << 32, v >> 32); + + v = (uint64_t)a1 * (uint64_t)b1; + *phigh += v; +#ifdef DEBUG_MULDIV + printf("mul: 0x%016llx * 0x%016llx = 0x%016llx%016llx\n", + a, b, *phigh, *plow); +#endif +} + +static void imul64(uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b) +{ + int sa, sb; + sa = (a < 0); + if (sa) + a = -a; + sb = (b < 0); + if (sb) + b = -b; + mul64(plow, phigh, a, b); + if (sa ^ sb) { + neg128(plow, phigh); + } +} + +/* return TRUE if overflow */ +static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b) +{ + uint64_t q, r, a1, a0; + int i, qb, ab; + + a0 = *plow; + a1 = *phigh; + if (a1 == 0) { + q = a0 / b; + r = a0 % b; + *plow = q; + *phigh = r; + } else { + if (a1 >= b) + return 1; + /* XXX: use a better algorithm */ + for(i = 0; i < 64; i++) { + ab = a1 >> 63; + a1 = (a1 << 1) | (a0 >> 63); + if (ab || a1 >= b) { + a1 -= b; + qb = 1; + } else { + qb = 0; + } + a0 = (a0 << 1) | qb; + } +#if defined(DEBUG_MULDIV) + printf("div: 0x%016llx%016llx / 0x%016llx: q=0x%016llx r=0x%016llx\n", + *phigh, *plow, b, a0, a1); +#endif + *plow = a0; + *phigh = a1; + } + return 0; +} + +/* return TRUE if overflow */ +static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b) +{ + int sa, sb; + sa = ((int64_t)*phigh < 0); + if (sa) + neg128(plow, phigh); + sb = (b < 0); + if (sb) + b = -b; + if (div64(plow, phigh, b) != 0) + return 1; + if (sa ^ sb) { + if (*plow > (1ULL << 63)) + return 1; + *plow = - *plow; + } else { + if (*plow >= (1ULL << 63)) + return 1; + } + if (sa) + *phigh = - *phigh; + return 0; +} + +void helper_mulq_EAX_T0(void) +{ + uint64_t r0, r1; + + mul64(&r0, &r1, EAX, T0); + EAX = r0; + EDX = r1; + CC_DST = r0; + CC_SRC = r1; +} + +void helper_imulq_EAX_T0(void) +{ + uint64_t r0, r1; + + imul64(&r0, &r1, EAX, T0); + EAX = r0; + EDX = r1; + CC_DST = r0; + CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63)); +} + +void helper_imulq_T0_T1(void) +{ + uint64_t r0, r1; + + imul64(&r0, &r1, T0, T1); + T0 = r0; + CC_DST = r0; + CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63)); +} + +void helper_divq_EAX_T0(void) +{ + uint64_t r0, r1; + if (T0 == 0) { + raise_exception(EXCP00_DIVZ); + } + r0 = EAX; + r1 = EDX; + if (div64(&r0, &r1, T0)) + raise_exception(EXCP00_DIVZ); + EAX = r0; + EDX = r1; +} + +void helper_idivq_EAX_T0(void) +{ + uint64_t r0, r1; + if (T0 == 0) { + raise_exception(EXCP00_DIVZ); + } + r0 = EAX; + r1 = EDX; + if (idiv64(&r0, &r1, T0)) + raise_exception(EXCP00_DIVZ); + EAX = r0; + EDX = r1; +} + +void helper_bswapq_T0(void) +{ + T0 = bswap64(T0); +} +#endif + +float approx_rsqrt(float a) +{ + return 1.0 / sqrt(a); +} + +float approx_rcp(float a) +{ + return 1.0 / a; +} + +void update_fp_status(void) +{ + int rnd_type; + + /* set rounding mode */ + switch(env->fpuc & RC_MASK) { + default: + case RC_NEAR: + rnd_type = float_round_nearest_even; + break; + case RC_DOWN: + rnd_type = float_round_down; + break; + case RC_UP: + rnd_type = float_round_up; + break; + case RC_CHOP: + rnd_type = float_round_to_zero; + break; + } + set_float_rounding_mode(rnd_type, &env->fp_status); +#ifdef FLOATX80 + switch((env->fpuc >> 8) & 3) { + case 0: + rnd_type = 32; + break; + case 2: + rnd_type = 64; + break; + case 3: + default: + rnd_type = 80; + break; + } + set_floatx80_rounding_precision(rnd_type, &env->fp_status); +#endif +} + +#if !defined(CONFIG_USER_ONLY) + +#define MMUSUFFIX _mmu +#define GETPC() (__builtin_return_address(0)) + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" + +#endif + +/* try to fill the TLB and return an exception if error. If retaddr is + NULL, it means that the function was called in C code (i.e. not + from generated code or from helper.c) */ +/* XXX: fix it to restore all registers */ +void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr) +{ + TranslationBlock *tb; + int ret; + unsigned long pc; + CPUX86State *saved_env; + + /* XXX: hack to restore env in all cases, even if not called from + generated code */ + saved_env = env; + env = cpu_single_env; + + ret = cpu_x86_handle_mmu_fault(env, addr, is_write, is_user, 1); + if (ret) { + if (retaddr) { + /* now we have a real cpu fault */ + pc = (unsigned long)retaddr; + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, NULL); + } + } + if (retaddr) + raise_exception_err(env->exception_index, env->error_code); + else + raise_exception_err_norestore(env->exception_index, env->error_code); + } + env = saved_env; +} diff --git a/tools/ioemu/target-i386/helper2.c b/tools/ioemu/target-i386/helper2.c new file mode 100644 index 0000000000..9d5d9b564e --- /dev/null +++ b/tools/ioemu/target-i386/helper2.c @@ -0,0 +1,1028 @@ +/* + * i386 helpers (without register variable usage) + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "exec-all.h" + +//#define DEBUG_MMU + +#ifdef USE_CODE_COPY +#include +#include +#include + +_syscall3(int, modify_ldt, int, func, void *, ptr, unsigned long, bytecount) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 66) +#define modify_ldt_ldt_s user_desc +#endif +#endif /* USE_CODE_COPY */ + +CPUX86State *cpu_x86_init(void) +{ + CPUX86State *env; + static int inited; + + env = qemu_mallocz(sizeof(CPUX86State)); + if (!env) + return NULL; + cpu_exec_init(env); + + /* init various static tables */ + if (!inited) { + inited = 1; + optimize_flags_init(); + } +#ifdef USE_CODE_COPY + /* testing code for code copy case */ + { + struct modify_ldt_ldt_s ldt; + + ldt.entry_number = 1; + ldt.base_addr = (unsigned long)env; + ldt.limit = (sizeof(CPUState) + 0xfff) >> 12; + ldt.seg_32bit = 1; + ldt.contents = MODIFY_LDT_CONTENTS_DATA; + ldt.read_exec_only = 0; + ldt.limit_in_pages = 1; + ldt.seg_not_present = 0; + ldt.useable = 1; + modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */ + + asm volatile ("movl %0, %%fs" : : "r" ((1 << 3) | 7)); + } +#endif + { + int family, model, stepping; +#ifdef TARGET_X86_64 + env->cpuid_vendor1 = 0x68747541; /* "Auth" */ + env->cpuid_vendor2 = 0x69746e65; /* "enti" */ + env->cpuid_vendor3 = 0x444d4163; /* "cAMD" */ + family = 6; + model = 2; + stepping = 3; +#else + env->cpuid_vendor1 = 0x756e6547; /* "Genu" */ + env->cpuid_vendor2 = 0x49656e69; /* "ineI" */ + env->cpuid_vendor3 = 0x6c65746e; /* "ntel" */ +#if 0 + /* pentium 75-200 */ + family = 5; + model = 2; + stepping = 11; +#else + /* pentium pro */ + family = 6; + model = 3; + stepping = 3; +#endif +#endif + env->cpuid_level = 2; + env->cpuid_version = (family << 8) | (model << 4) | stepping; + env->cpuid_features = (CPUID_FP87 | CPUID_DE | CPUID_PSE | + CPUID_TSC | CPUID_MSR | CPUID_MCE | + CPUID_CX8 | CPUID_PGE | CPUID_CMOV | + CPUID_PAT); + env->pat = 0x0007040600070406ULL; + env->cpuid_ext_features = CPUID_EXT_SSE3; + env->cpuid_features |= CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | CPUID_PAE | CPUID_SEP; + env->cpuid_features |= CPUID_APIC; + env->cpuid_xlevel = 0; + { + const char *model_id = "QEMU Virtual CPU version " QEMU_VERSION; + int c, len, i; + len = strlen(model_id); + for(i = 0; i < 48; i++) { + if (i >= len) + c = '\0'; + else + c = model_id[i]; + env->cpuid_model[i >> 2] |= c << (8 * (i & 3)); + } + } +#ifdef TARGET_X86_64 + /* currently not enabled for std i386 because not fully tested */ + env->cpuid_ext2_features = (env->cpuid_features & 0x0183F3FF); + env->cpuid_ext2_features |= CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX; + env->cpuid_xlevel = 0x80000008; + + /* these features are needed for Win64 and aren't fully implemented */ + env->cpuid_features |= CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA; +#endif + } + cpu_reset(env); +#ifdef USE_KQEMU + kqemu_init(env); +#endif + return env; +} + +/* NOTE: must be called outside the CPU execute loop */ +void cpu_reset(CPUX86State *env) +{ + int i; + + memset(env, 0, offsetof(CPUX86State, breakpoints)); + + tlb_flush(env, 1); + + /* init to reset state */ + +#ifdef CONFIG_SOFTMMU + env->hflags |= HF_SOFTMMU_MASK; +#endif + + cpu_x86_update_cr0(env, 0x60000010); + env->a20_mask = 0xffffffff; + + env->idt.limit = 0xffff; + env->gdt.limit = 0xffff; + env->ldt.limit = 0xffff; + env->ldt.flags = DESC_P_MASK; + env->tr.limit = 0xffff; + env->tr.flags = DESC_P_MASK; + + cpu_x86_load_seg_cache(env, R_CS, 0xf000, 0xffff0000, 0xffff, 0); + cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffff, 0); + cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffff, 0); + cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffff, 0); + cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffff, 0); + cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffff, 0); + + env->eip = 0xfff0; + env->regs[R_EDX] = 0x600; /* indicate P6 processor */ + + env->eflags = 0x2; + + /* FPU init */ + for(i = 0;i < 8; i++) + env->fptags[i] = 1; + env->fpuc = 0x37f; + + env->mxcsr = 0x1f80; +} + +void cpu_x86_close(CPUX86State *env) +{ + free(env); +} + +/***********************************************************/ +/* x86 debug */ + +static const char *cc_op_str[] = { + "DYNAMIC", + "EFLAGS", + + "MULB", + "MULW", + "MULL", + "MULQ", + + "ADDB", + "ADDW", + "ADDL", + "ADDQ", + + "ADCB", + "ADCW", + "ADCL", + "ADCQ", + + "SUBB", + "SUBW", + "SUBL", + "SUBQ", + + "SBBB", + "SBBW", + "SBBL", + "SBBQ", + + "LOGICB", + "LOGICW", + "LOGICL", + "LOGICQ", + + "INCB", + "INCW", + "INCL", + "INCQ", + + "DECB", + "DECW", + "DECL", + "DECQ", + + "SHLB", + "SHLW", + "SHLL", + "SHLQ", + + "SARB", + "SARW", + "SARL", + "SARQ", +}; + +void cpu_dump_state(CPUState *env, FILE *f, + int (*cpu_fprintf)(FILE *f, const char *fmt, ...), + int flags) +{ + int eflags, i, nb; + char cc_op_name[32]; + static const char *seg_name[6] = { "ES", "CS", "SS", "DS", "FS", "GS" }; + + eflags = env->eflags; +#ifdef TARGET_X86_64 + if (env->hflags & HF_CS64_MASK) { + cpu_fprintf(f, + "RAX=%016llx RBX=%016llx RCX=%016llx RDX=%016llx\n" + "RSI=%016llx RDI=%016llx RBP=%016llx RSP=%016llx\n" + "R8 =%016llx R9 =%016llx R10=%016llx R11=%016llx\n" + "R12=%016llx R13=%016llx R14=%016llx R15=%016llx\n" + "RIP=%016llx RFL=%08x [%c%c%c%c%c%c%c] CPL=%d II=%d A20=%d HLT=%d\n", + env->regs[R_EAX], + env->regs[R_EBX], + env->regs[R_ECX], + env->regs[R_EDX], + env->regs[R_ESI], + env->regs[R_EDI], + env->regs[R_EBP], + env->regs[R_ESP], + env->regs[8], + env->regs[9], + env->regs[10], + env->regs[11], + env->regs[12], + env->regs[13], + env->regs[14], + env->regs[15], + env->eip, eflags, + eflags & DF_MASK ? 'D' : '-', + eflags & CC_O ? 'O' : '-', + eflags & CC_S ? 'S' : '-', + eflags & CC_Z ? 'Z' : '-', + eflags & CC_A ? 'A' : '-', + eflags & CC_P ? 'P' : '-', + eflags & CC_C ? 'C' : '-', + env->hflags & HF_CPL_MASK, + (env->hflags >> HF_INHIBIT_IRQ_SHIFT) & 1, + (env->a20_mask >> 20) & 1, + (env->hflags >> HF_HALTED_SHIFT) & 1); + } else +#endif + { + cpu_fprintf(f, "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n" + "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n" + "EIP=%08x EFL=%08x [%c%c%c%c%c%c%c] CPL=%d II=%d A20=%d HLT=%d\n", + (uint32_t)env->regs[R_EAX], + (uint32_t)env->regs[R_EBX], + (uint32_t)env->regs[R_ECX], + (uint32_t)env->regs[R_EDX], + (uint32_t)env->regs[R_ESI], + (uint32_t)env->regs[R_EDI], + (uint32_t)env->regs[R_EBP], + (uint32_t)env->regs[R_ESP], + (uint32_t)env->eip, eflags, + eflags & DF_MASK ? 'D' : '-', + eflags & CC_O ? 'O' : '-', + eflags & CC_S ? 'S' : '-', + eflags & CC_Z ? 'Z' : '-', + eflags & CC_A ? 'A' : '-', + eflags & CC_P ? 'P' : '-', + eflags & CC_C ? 'C' : '-', + env->hflags & HF_CPL_MASK, + (env->hflags >> HF_INHIBIT_IRQ_SHIFT) & 1, + (env->a20_mask >> 20) & 1, + (env->hflags >> HF_HALTED_SHIFT) & 1); + } + +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + for(i = 0; i < 6; i++) { + SegmentCache *sc = &env->segs[i]; + cpu_fprintf(f, "%s =%04x %016llx %08x %08x\n", + seg_name[i], + sc->selector, + sc->base, + sc->limit, + sc->flags); + } + cpu_fprintf(f, "LDT=%04x %016llx %08x %08x\n", + env->ldt.selector, + env->ldt.base, + env->ldt.limit, + env->ldt.flags); + cpu_fprintf(f, "TR =%04x %016llx %08x %08x\n", + env->tr.selector, + env->tr.base, + env->tr.limit, + env->tr.flags); + cpu_fprintf(f, "GDT= %016llx %08x\n", + env->gdt.base, env->gdt.limit); + cpu_fprintf(f, "IDT= %016llx %08x\n", + env->idt.base, env->idt.limit); + cpu_fprintf(f, "CR0=%08x CR2=%016llx CR3=%016llx CR4=%08x\n", + (uint32_t)env->cr[0], + env->cr[2], + env->cr[3], + (uint32_t)env->cr[4]); + } else +#endif + { + for(i = 0; i < 6; i++) { + SegmentCache *sc = &env->segs[i]; + cpu_fprintf(f, "%s =%04x %08x %08x %08x\n", + seg_name[i], + sc->selector, + (uint32_t)sc->base, + sc->limit, + sc->flags); + } + cpu_fprintf(f, "LDT=%04x %08x %08x %08x\n", + env->ldt.selector, + (uint32_t)env->ldt.base, + env->ldt.limit, + env->ldt.flags); + cpu_fprintf(f, "TR =%04x %08x %08x %08x\n", + env->tr.selector, + (uint32_t)env->tr.base, + env->tr.limit, + env->tr.flags); + cpu_fprintf(f, "GDT= %08x %08x\n", + (uint32_t)env->gdt.base, env->gdt.limit); + cpu_fprintf(f, "IDT= %08x %08x\n", + (uint32_t)env->idt.base, env->idt.limit); + cpu_fprintf(f, "CR0=%08x CR2=%08x CR3=%08x CR4=%08x\n", + (uint32_t)env->cr[0], + (uint32_t)env->cr[2], + (uint32_t)env->cr[3], + (uint32_t)env->cr[4]); + } + if (flags & X86_DUMP_CCOP) { + if ((unsigned)env->cc_op < CC_OP_NB) + snprintf(cc_op_name, sizeof(cc_op_name), "%s", cc_op_str[env->cc_op]); + else + snprintf(cc_op_name, sizeof(cc_op_name), "[%d]", env->cc_op); +#ifdef TARGET_X86_64 + if (env->hflags & HF_CS64_MASK) { + cpu_fprintf(f, "CCS=%016llx CCD=%016llx CCO=%-8s\n", + env->cc_src, env->cc_dst, + cc_op_name); + } else +#endif + { + cpu_fprintf(f, "CCS=%08x CCD=%08x CCO=%-8s\n", + (uint32_t)env->cc_src, (uint32_t)env->cc_dst, + cc_op_name); + } + } + if (flags & X86_DUMP_FPU) { + int fptag; + fptag = 0; + for(i = 0; i < 8; i++) { + fptag |= ((!env->fptags[i]) << i); + } + cpu_fprintf(f, "FCW=%04x FSW=%04x [ST=%d] FTW=%02x MXCSR=%08x\n", + env->fpuc, + (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11, + env->fpstt, + fptag, + env->mxcsr); + for(i=0;i<8;i++) { +#if defined(USE_X86LDOUBLE) + union { + long double d; + struct { + uint64_t lower; + uint16_t upper; + } l; + } tmp; + tmp.d = env->fpregs[i].d; + cpu_fprintf(f, "FPR%d=%016llx %04x", + i, tmp.l.lower, tmp.l.upper); +#else + cpu_fprintf(f, "FPR%d=%016llx", + i, env->fpregs[i].mmx.q); +#endif + if ((i & 1) == 1) + cpu_fprintf(f, "\n"); + else + cpu_fprintf(f, " "); + } + if (env->hflags & HF_CS64_MASK) + nb = 16; + else + nb = 8; + for(i=0;ixmm_regs[i].XMM_L(3), + env->xmm_regs[i].XMM_L(2), + env->xmm_regs[i].XMM_L(1), + env->xmm_regs[i].XMM_L(0)); + if ((i & 1) == 1) + cpu_fprintf(f, "\n"); + else + cpu_fprintf(f, " "); + } + } +} + +/***********************************************************/ +/* x86 mmu */ +/* XXX: add PGE support */ + +void cpu_x86_set_a20(CPUX86State *env, int a20_state) +{ + a20_state = (a20_state != 0); + if (a20_state != ((env->a20_mask >> 20) & 1)) { +#if defined(DEBUG_MMU) + printf("A20 update: a20=%d\n", a20_state); +#endif + /* if the cpu is currently executing code, we must unlink it and + all the potentially executing TB */ + cpu_interrupt(env, CPU_INTERRUPT_EXITTB); + + /* when a20 is changed, all the MMU mappings are invalid, so + we must flush everything */ + tlb_flush(env, 1); + env->a20_mask = 0xffefffff | (a20_state << 20); + } +} + +void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0) +{ + int pe_state; + +#if defined(DEBUG_MMU) + printf("CR0 update: CR0=0x%08x\n", new_cr0); +#endif + if ((new_cr0 & (CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK)) != + (env->cr[0] & (CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK))) { + tlb_flush(env, 1); + } + +#ifdef TARGET_X86_64 + if (!(env->cr[0] & CR0_PG_MASK) && (new_cr0 & CR0_PG_MASK) && + (env->efer & MSR_EFER_LME)) { + /* enter in long mode */ + /* XXX: generate an exception */ + if (!(env->cr[4] & CR4_PAE_MASK)) + return; + env->efer |= MSR_EFER_LMA; + env->hflags |= HF_LMA_MASK; + } else if ((env->cr[0] & CR0_PG_MASK) && !(new_cr0 & CR0_PG_MASK) && + (env->efer & MSR_EFER_LMA)) { + /* exit long mode */ + env->efer &= ~MSR_EFER_LMA; + env->hflags &= ~(HF_LMA_MASK | HF_CS64_MASK); + env->eip &= 0xffffffff; + } +#endif + env->cr[0] = new_cr0 | CR0_ET_MASK; + + /* update PE flag in hidden flags */ + pe_state = (env->cr[0] & CR0_PE_MASK); + env->hflags = (env->hflags & ~HF_PE_MASK) | (pe_state << HF_PE_SHIFT); + /* ensure that ADDSEG is always set in real mode */ + env->hflags |= ((pe_state ^ 1) << HF_ADDSEG_SHIFT); + /* update FPU flags */ + env->hflags = (env->hflags & ~(HF_MP_MASK | HF_EM_MASK | HF_TS_MASK)) | + ((new_cr0 << (HF_MP_SHIFT - 1)) & (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK)); +} + +/* XXX: in legacy PAE mode, generate a GPF if reserved bits are set in + the PDPT */ +void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3) +{ + env->cr[3] = new_cr3; + if (env->cr[0] & CR0_PG_MASK) { +#if defined(DEBUG_MMU) + printf("CR3 update: CR3=" TARGET_FMT_lx "\n", new_cr3); +#endif + tlb_flush(env, 0); + } +} + +void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) +{ +#if defined(DEBUG_MMU) + printf("CR4 update: CR4=%08x\n", (uint32_t)env->cr[4]); +#endif + if ((new_cr4 & (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK)) != + (env->cr[4] & (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK))) { + tlb_flush(env, 1); + } + /* SSE handling */ + if (!(env->cpuid_features & CPUID_SSE)) + new_cr4 &= ~CR4_OSFXSR_MASK; + if (new_cr4 & CR4_OSFXSR_MASK) + env->hflags |= HF_OSFXSR_MASK; + else + env->hflags &= ~HF_OSFXSR_MASK; + + env->cr[4] = new_cr4; +} + +/* XXX: also flush 4MB pages */ +void cpu_x86_flush_tlb(CPUX86State *env, target_ulong addr) +{ + tlb_flush_page(env, addr); +} + +#if defined(CONFIG_USER_ONLY) + +int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, + int is_write, int is_user, int is_softmmu) +{ + /* user mode only emulation */ + is_write &= 1; + env->cr[2] = addr; + env->error_code = (is_write << PG_ERROR_W_BIT); + env->error_code |= PG_ERROR_U_MASK; + env->exception_index = EXCP0E_PAGE; + return 1; +} + +target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + return addr; +} + +#else + +#define PHYS_ADDR_MASK 0xfffff000 + +/* return value: + -1 = cannot handle fault + 0 = nothing more to do + 1 = generate PF fault + 2 = soft MMU activation required for this block +*/ +int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, + int is_write1, int is_user, int is_softmmu) +{ + uint64_t ptep, pte; + uint32_t pdpe_addr, pde_addr, pte_addr; + int error_code, is_dirty, prot, page_size, ret, is_write; + unsigned long paddr, page_offset; + target_ulong vaddr, virt_addr; + +#if defined(DEBUG_MMU) + printf("MMU fault: addr=" TARGET_FMT_lx " w=%d u=%d eip=" TARGET_FMT_lx "\n", + addr, is_write1, is_user, env->eip); +#endif + is_write = is_write1 & 1; + + if (!(env->cr[0] & CR0_PG_MASK)) { + pte = addr; + virt_addr = addr & TARGET_PAGE_MASK; + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + page_size = 4096; + goto do_mapping; + } + + if (env->cr[4] & CR4_PAE_MASK) { + uint64_t pde, pdpe; + + /* XXX: we only use 32 bit physical addresses */ +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + uint32_t pml4e_addr; + uint64_t pml4e; + int32_t sext; + + /* test virtual address sign extension */ + sext = (int64_t)addr >> 47; + if (sext != 0 && sext != -1) { + env->error_code = 0; + env->exception_index = EXCP0D_GPF; + return 1; + } + + pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) & + env->a20_mask; + pml4e = ldq_phys(pml4e_addr); + if (!(pml4e & PG_PRESENT_MASK)) { + error_code = 0; + goto do_fault; + } + if (!(env->efer & MSR_EFER_NXE) && (pml4e & PG_NX_MASK)) { + error_code = PG_ERROR_RSVD_MASK; + goto do_fault; + } + if (!(pml4e & PG_ACCESSED_MASK)) { + pml4e |= PG_ACCESSED_MASK; + stl_phys_notdirty(pml4e_addr, pml4e); + } + ptep = pml4e ^ PG_NX_MASK; + pdpe_addr = ((pml4e & PHYS_ADDR_MASK) + (((addr >> 30) & 0x1ff) << 3)) & + env->a20_mask; + pdpe = ldq_phys(pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) { + error_code = 0; + goto do_fault; + } + if (!(env->efer & MSR_EFER_NXE) && (pdpe & PG_NX_MASK)) { + error_code = PG_ERROR_RSVD_MASK; + goto do_fault; + } + ptep &= pdpe ^ PG_NX_MASK; + if (!(pdpe & PG_ACCESSED_MASK)) { + pdpe |= PG_ACCESSED_MASK; + stl_phys_notdirty(pdpe_addr, pdpe); + } + } else +#endif + { + /* XXX: load them when cr3 is loaded ? */ + pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 30) << 3)) & + env->a20_mask; + pdpe = ldq_phys(pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) { + error_code = 0; + goto do_fault; + } + ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; + } + + pde_addr = ((pdpe & PHYS_ADDR_MASK) + (((addr >> 21) & 0x1ff) << 3)) & + env->a20_mask; + pde = ldq_phys(pde_addr); + if (!(pde & PG_PRESENT_MASK)) { + error_code = 0; + goto do_fault; + } + if (!(env->efer & MSR_EFER_NXE) && (pde & PG_NX_MASK)) { + error_code = PG_ERROR_RSVD_MASK; + goto do_fault; + } + ptep &= pde ^ PG_NX_MASK; + if (pde & PG_PSE_MASK) { + /* 2 MB page */ + page_size = 2048 * 1024; + ptep ^= PG_NX_MASK; + if ((ptep & PG_NX_MASK) && is_write1 == 2) + goto do_fault_protect; + if (is_user) { + if (!(ptep & PG_USER_MASK)) + goto do_fault_protect; + if (is_write && !(ptep & PG_RW_MASK)) + goto do_fault_protect; + } else { + if ((env->cr[0] & CR0_WP_MASK) && + is_write && !(ptep & PG_RW_MASK)) + goto do_fault_protect; + } + is_dirty = is_write && !(pde & PG_DIRTY_MASK); + if (!(pde & PG_ACCESSED_MASK) || is_dirty) { + pde |= PG_ACCESSED_MASK; + if (is_dirty) + pde |= PG_DIRTY_MASK; + stl_phys_notdirty(pde_addr, pde); + } + /* align to page_size */ + pte = pde & ((PHYS_ADDR_MASK & ~(page_size - 1)) | 0xfff); + virt_addr = addr & ~(page_size - 1); + } else { + /* 4 KB page */ + if (!(pde & PG_ACCESSED_MASK)) { + pde |= PG_ACCESSED_MASK; + stl_phys_notdirty(pde_addr, pde); + } + pte_addr = ((pde & PHYS_ADDR_MASK) + (((addr >> 12) & 0x1ff) << 3)) & + env->a20_mask; + pte = ldq_phys(pte_addr); + if (!(pte & PG_PRESENT_MASK)) { + error_code = 0; + goto do_fault; + } + if (!(env->efer & MSR_EFER_NXE) && (pte & PG_NX_MASK)) { + error_code = PG_ERROR_RSVD_MASK; + goto do_fault; + } + /* combine pde and pte nx, user and rw protections */ + ptep &= pte ^ PG_NX_MASK; + ptep ^= PG_NX_MASK; + if ((ptep & PG_NX_MASK) && is_write1 == 2) + goto do_fault_protect; + if (is_user) { + if (!(ptep & PG_USER_MASK)) + goto do_fault_protect; + if (is_write && !(ptep & PG_RW_MASK)) + goto do_fault_protect; + } else { + if ((env->cr[0] & CR0_WP_MASK) && + is_write && !(ptep & PG_RW_MASK)) + goto do_fault_protect; + } + is_dirty = is_write && !(pte & PG_DIRTY_MASK); + if (!(pte & PG_ACCESSED_MASK) || is_dirty) { + pte |= PG_ACCESSED_MASK; + if (is_dirty) + pte |= PG_DIRTY_MASK; + stl_phys_notdirty(pte_addr, pte); + } + page_size = 4096; + virt_addr = addr & ~0xfff; + pte = pte & (PHYS_ADDR_MASK | 0xfff); + } + } else { + uint32_t pde; + + /* page directory entry */ + pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & ~3)) & + env->a20_mask; + pde = ldl_phys(pde_addr); + if (!(pde & PG_PRESENT_MASK)) { + error_code = 0; + goto do_fault; + } + /* if PSE bit is set, then we use a 4MB page */ + if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { + page_size = 4096 * 1024; + if (is_user) { + if (!(pde & PG_USER_MASK)) + goto do_fault_protect; + if (is_write && !(pde & PG_RW_MASK)) + goto do_fault_protect; + } else { + if ((env->cr[0] & CR0_WP_MASK) && + is_write && !(pde & PG_RW_MASK)) + goto do_fault_protect; + } + is_dirty = is_write && !(pde & PG_DIRTY_MASK); + if (!(pde & PG_ACCESSED_MASK) || is_dirty) { + pde |= PG_ACCESSED_MASK; + if (is_dirty) + pde |= PG_DIRTY_MASK; + stl_phys_notdirty(pde_addr, pde); + } + + pte = pde & ~( (page_size - 1) & ~0xfff); /* align to page_size */ + ptep = pte; + virt_addr = addr & ~(page_size - 1); + } else { + if (!(pde & PG_ACCESSED_MASK)) { + pde |= PG_ACCESSED_MASK; + stl_phys_notdirty(pde_addr, pde); + } + + /* page directory entry */ + pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & + env->a20_mask; + pte = ldl_phys(pte_addr); + if (!(pte & PG_PRESENT_MASK)) { + error_code = 0; + goto do_fault; + } + /* combine pde and pte user and rw protections */ + ptep = pte & pde; + if (is_user) { + if (!(ptep & PG_USER_MASK)) + goto do_fault_protect; + if (is_write && !(ptep & PG_RW_MASK)) + goto do_fault_protect; + } else { + if ((env->cr[0] & CR0_WP_MASK) && + is_write && !(ptep & PG_RW_MASK)) + goto do_fault_protect; + } + is_dirty = is_write && !(pte & PG_DIRTY_MASK); + if (!(pte & PG_ACCESSED_MASK) || is_dirty) { + pte |= PG_ACCESSED_MASK; + if (is_dirty) + pte |= PG_DIRTY_MASK; + stl_phys_notdirty(pte_addr, pte); + } + page_size = 4096; + virt_addr = addr & ~0xfff; + } + } + /* the page can be put in the TLB */ + prot = PAGE_READ; + if (!(ptep & PG_NX_MASK)) + prot |= PAGE_EXEC; + if (pte & PG_DIRTY_MASK) { + /* only set write access if already dirty... otherwise wait + for dirty access */ + if (is_user) { + if (ptep & PG_RW_MASK) + prot |= PAGE_WRITE; + } else { + if (!(env->cr[0] & CR0_WP_MASK) || + (ptep & PG_RW_MASK)) + prot |= PAGE_WRITE; + } + } + do_mapping: + pte = pte & env->a20_mask; + + /* Even if 4MB pages, we map only one 4KB page in the cache to + avoid filling it too fast */ + page_offset = (addr & TARGET_PAGE_MASK) & (page_size - 1); + paddr = (pte & TARGET_PAGE_MASK) + page_offset; + vaddr = virt_addr + page_offset; + + ret = tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu); + return ret; + do_fault_protect: + error_code = PG_ERROR_P_MASK; + do_fault: + env->cr[2] = addr; + error_code |= (is_write << PG_ERROR_W_BIT); + if (is_user) + error_code |= PG_ERROR_U_MASK; + if (is_write1 == 2 && + (env->efer & MSR_EFER_NXE) && + (env->cr[4] & CR4_PAE_MASK)) + error_code |= PG_ERROR_I_D_MASK; + env->error_code = error_code; + env->exception_index = EXCP0E_PAGE; + return 1; +} + +target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + uint32_t pde_addr, pte_addr; + uint32_t pde, pte, paddr, page_offset, page_size; + + if (env->cr[4] & CR4_PAE_MASK) { + uint32_t pdpe_addr, pde_addr, pte_addr; + uint32_t pdpe; + + /* XXX: we only use 32 bit physical addresses */ +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + uint32_t pml4e_addr, pml4e; + int32_t sext; + + /* test virtual address sign extension */ + sext = (int64_t)addr >> 47; + if (sext != 0 && sext != -1) + return -1; + + pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) & + env->a20_mask; + pml4e = ldl_phys(pml4e_addr); + if (!(pml4e & PG_PRESENT_MASK)) + return -1; + + pdpe_addr = ((pml4e & ~0xfff) + (((addr >> 30) & 0x1ff) << 3)) & + env->a20_mask; + pdpe = ldl_phys(pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) + return -1; + } else +#endif + { + pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 30) << 3)) & + env->a20_mask; + pdpe = ldl_phys(pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) + return -1; + } + + pde_addr = ((pdpe & ~0xfff) + (((addr >> 21) & 0x1ff) << 3)) & + env->a20_mask; + pde = ldl_phys(pde_addr); + if (!(pde & PG_PRESENT_MASK)) { + return -1; + } + if (pde & PG_PSE_MASK) { + /* 2 MB page */ + page_size = 2048 * 1024; + pte = pde & ~( (page_size - 1) & ~0xfff); /* align to page_size */ + } else { + /* 4 KB page */ + pte_addr = ((pde & ~0xfff) + (((addr >> 12) & 0x1ff) << 3)) & + env->a20_mask; + page_size = 4096; + pte = ldl_phys(pte_addr); + } + } else { + if (!(env->cr[0] & CR0_PG_MASK)) { + pte = addr; + page_size = 4096; + } else { + /* page directory entry */ + pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & ~3)) & env->a20_mask; + pde = ldl_phys(pde_addr); + if (!(pde & PG_PRESENT_MASK)) + return -1; + if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { + pte = pde & ~0x003ff000; /* align to 4MB */ + page_size = 4096 * 1024; + } else { + /* page directory entry */ + pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & env->a20_mask; + pte = ldl_phys(pte_addr); + if (!(pte & PG_PRESENT_MASK)) + return -1; + page_size = 4096; + } + } + pte = pte & env->a20_mask; + } + + page_offset = (addr & TARGET_PAGE_MASK) & (page_size - 1); + paddr = (pte & TARGET_PAGE_MASK) + page_offset; + return paddr; +} +#endif /* !CONFIG_USER_ONLY */ + +#if defined(USE_CODE_COPY) +struct fpstate { + uint16_t fpuc; + uint16_t dummy1; + uint16_t fpus; + uint16_t dummy2; + uint16_t fptag; + uint16_t dummy3; + + uint32_t fpip; + uint32_t fpcs; + uint32_t fpoo; + uint32_t fpos; + uint8_t fpregs1[8 * 10]; +}; + +void restore_native_fp_state(CPUState *env) +{ + int fptag, i, j; + struct fpstate fp1, *fp = &fp1; + + fp->fpuc = env->fpuc; + fp->fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + fptag = 0; + for (i=7; i>=0; i--) { + fptag <<= 2; + if (env->fptags[i]) { + fptag |= 3; + } else { + /* the FPU automatically computes it */ + } + } + fp->fptag = fptag; + j = env->fpstt; + for(i = 0;i < 8; i++) { + memcpy(&fp->fpregs1[i * 10], &env->fpregs[j].d, 10); + j = (j + 1) & 7; + } + asm volatile ("frstor %0" : "=m" (*fp)); + env->native_fp_regs = 1; +} + +void save_native_fp_state(CPUState *env) +{ + int fptag, i, j; + uint16_t fpuc; + struct fpstate fp1, *fp = &fp1; + + asm volatile ("fsave %0" : : "m" (*fp)); + env->fpuc = fp->fpuc; + env->fpstt = (fp->fpus >> 11) & 7; + env->fpus = fp->fpus & ~0x3800; + fptag = fp->fptag; + for(i = 0;i < 8; i++) { + env->fptags[i] = ((fptag & 3) == 3); + fptag >>= 2; + } + j = env->fpstt; + for(i = 0;i < 8; i++) { + memcpy(&env->fpregs[j].d, &fp->fpregs1[i * 10], 10); + j = (j + 1) & 7; + } + /* we must restore the default rounding state */ + /* XXX: we do not restore the exception state */ + fpuc = 0x037f | (env->fpuc & (3 << 10)); + asm volatile("fldcw %0" : : "m" (fpuc)); + env->native_fp_regs = 0; +} +#endif diff --git a/tools/ioemu/target-i386/op.c b/tools/ioemu/target-i386/op.c new file mode 100644 index 0000000000..a9a8665a1c --- /dev/null +++ b/tools/ioemu/target-i386/op.c @@ -0,0 +1,2437 @@ +/* + * i386 micro operations + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define ASM_SOFTMMU +#include "exec.h" + +/* n must be a constant to be efficient */ +static inline target_long lshift(target_long x, int n) +{ + if (n >= 0) + return x << n; + else + return x >> (-n); +} + +/* we define the various pieces of code used by the JIT */ + +#define REG EAX +#define REGNAME _EAX +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG ECX +#define REGNAME _ECX +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG EDX +#define REGNAME _EDX +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG EBX +#define REGNAME _EBX +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG ESP +#define REGNAME _ESP +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG EBP +#define REGNAME _EBP +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG ESI +#define REGNAME _ESI +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG EDI +#define REGNAME _EDI +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#ifdef TARGET_X86_64 + +#define REG (env->regs[8]) +#define REGNAME _R8 +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG (env->regs[9]) +#define REGNAME _R9 +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG (env->regs[10]) +#define REGNAME _R10 +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG (env->regs[11]) +#define REGNAME _R11 +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG (env->regs[12]) +#define REGNAME _R12 +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG (env->regs[13]) +#define REGNAME _R13 +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG (env->regs[14]) +#define REGNAME _R14 +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#define REG (env->regs[15]) +#define REGNAME _R15 +#include "opreg_template.h" +#undef REG +#undef REGNAME + +#endif + +/* operations with flags */ + +/* update flags with T0 and T1 (add/sub case) */ +void OPPROTO op_update2_cc(void) +{ + CC_SRC = T1; + CC_DST = T0; +} + +/* update flags with T0 (logic operation case) */ +void OPPROTO op_update1_cc(void) +{ + CC_DST = T0; +} + +void OPPROTO op_update_neg_cc(void) +{ + CC_SRC = -T0; + CC_DST = T0; +} + +void OPPROTO op_cmpl_T0_T1_cc(void) +{ + CC_SRC = T1; + CC_DST = T0 - T1; +} + +void OPPROTO op_update_inc_cc(void) +{ + CC_SRC = cc_table[CC_OP].compute_c(); + CC_DST = T0; +} + +void OPPROTO op_testl_T0_T1_cc(void) +{ + CC_DST = T0 & T1; +} + +/* operations without flags */ + +void OPPROTO op_addl_T0_T1(void) +{ + T0 += T1; +} + +void OPPROTO op_orl_T0_T1(void) +{ + T0 |= T1; +} + +void OPPROTO op_andl_T0_T1(void) +{ + T0 &= T1; +} + +void OPPROTO op_subl_T0_T1(void) +{ + T0 -= T1; +} + +void OPPROTO op_xorl_T0_T1(void) +{ + T0 ^= T1; +} + +void OPPROTO op_negl_T0(void) +{ + T0 = -T0; +} + +void OPPROTO op_incl_T0(void) +{ + T0++; +} + +void OPPROTO op_decl_T0(void) +{ + T0--; +} + +void OPPROTO op_notl_T0(void) +{ + T0 = ~T0; +} + +void OPPROTO op_bswapl_T0(void) +{ + T0 = bswap32(T0); +} + +#ifdef TARGET_X86_64 +void OPPROTO op_bswapq_T0(void) +{ + helper_bswapq_T0(); +} +#endif + +/* multiply/divide */ + +/* XXX: add eflags optimizations */ +/* XXX: add non P4 style flags */ + +void OPPROTO op_mulb_AL_T0(void) +{ + unsigned int res; + res = (uint8_t)EAX * (uint8_t)T0; + EAX = (EAX & ~0xffff) | res; + CC_DST = res; + CC_SRC = (res & 0xff00); +} + +void OPPROTO op_imulb_AL_T0(void) +{ + int res; + res = (int8_t)EAX * (int8_t)T0; + EAX = (EAX & ~0xffff) | (res & 0xffff); + CC_DST = res; + CC_SRC = (res != (int8_t)res); +} + +void OPPROTO op_mulw_AX_T0(void) +{ + unsigned int res; + res = (uint16_t)EAX * (uint16_t)T0; + EAX = (EAX & ~0xffff) | (res & 0xffff); + EDX = (EDX & ~0xffff) | ((res >> 16) & 0xffff); + CC_DST = res; + CC_SRC = res >> 16; +} + +void OPPROTO op_imulw_AX_T0(void) +{ + int res; + res = (int16_t)EAX * (int16_t)T0; + EAX = (EAX & ~0xffff) | (res & 0xffff); + EDX = (EDX & ~0xffff) | ((res >> 16) & 0xffff); + CC_DST = res; + CC_SRC = (res != (int16_t)res); +} + +void OPPROTO op_mull_EAX_T0(void) +{ + uint64_t res; + res = (uint64_t)((uint32_t)EAX) * (uint64_t)((uint32_t)T0); + EAX = (uint32_t)res; + EDX = (uint32_t)(res >> 32); + CC_DST = (uint32_t)res; + CC_SRC = (uint32_t)(res >> 32); +} + +void OPPROTO op_imull_EAX_T0(void) +{ + int64_t res; + res = (int64_t)((int32_t)EAX) * (int64_t)((int32_t)T0); + EAX = (uint32_t)(res); + EDX = (uint32_t)(res >> 32); + CC_DST = res; + CC_SRC = (res != (int32_t)res); +} + +void OPPROTO op_imulw_T0_T1(void) +{ + int res; + res = (int16_t)T0 * (int16_t)T1; + T0 = res; + CC_DST = res; + CC_SRC = (res != (int16_t)res); +} + +void OPPROTO op_imull_T0_T1(void) +{ + int64_t res; + res = (int64_t)((int32_t)T0) * (int64_t)((int32_t)T1); + T0 = res; + CC_DST = res; + CC_SRC = (res != (int32_t)res); +} + +#ifdef TARGET_X86_64 +void OPPROTO op_mulq_EAX_T0(void) +{ + helper_mulq_EAX_T0(); +} + +void OPPROTO op_imulq_EAX_T0(void) +{ + helper_imulq_EAX_T0(); +} + +void OPPROTO op_imulq_T0_T1(void) +{ + helper_imulq_T0_T1(); +} +#endif + +/* division, flags are undefined */ + +void OPPROTO op_divb_AL_T0(void) +{ + unsigned int num, den, q, r; + + num = (EAX & 0xffff); + den = (T0 & 0xff); + if (den == 0) { + raise_exception(EXCP00_DIVZ); + } + q = (num / den); + if (q > 0xff) + raise_exception(EXCP00_DIVZ); + q &= 0xff; + r = (num % den) & 0xff; + EAX = (EAX & ~0xffff) | (r << 8) | q; +} + +void OPPROTO op_idivb_AL_T0(void) +{ + int num, den, q, r; + + num = (int16_t)EAX; + den = (int8_t)T0; + if (den == 0) { + raise_exception(EXCP00_DIVZ); + } + q = (num / den); + if (q != (int8_t)q) + raise_exception(EXCP00_DIVZ); + q &= 0xff; + r = (num % den) & 0xff; + EAX = (EAX & ~0xffff) | (r << 8) | q; +} + +void OPPROTO op_divw_AX_T0(void) +{ + unsigned int num, den, q, r; + + num = (EAX & 0xffff) | ((EDX & 0xffff) << 16); + den = (T0 & 0xffff); + if (den == 0) { + raise_exception(EXCP00_DIVZ); + } + q = (num / den); + if (q > 0xffff) + raise_exception(EXCP00_DIVZ); + q &= 0xffff; + r = (num % den) & 0xffff; + EAX = (EAX & ~0xffff) | q; + EDX = (EDX & ~0xffff) | r; +} + +void OPPROTO op_idivw_AX_T0(void) +{ + int num, den, q, r; + + num = (EAX & 0xffff) | ((EDX & 0xffff) << 16); + den = (int16_t)T0; + if (den == 0) { + raise_exception(EXCP00_DIVZ); + } + q = (num / den); + if (q != (int16_t)q) + raise_exception(EXCP00_DIVZ); + q &= 0xffff; + r = (num % den) & 0xffff; + EAX = (EAX & ~0xffff) | q; + EDX = (EDX & ~0xffff) | r; +} + +void OPPROTO op_divl_EAX_T0(void) +{ + helper_divl_EAX_T0(); +} + +void OPPROTO op_idivl_EAX_T0(void) +{ + helper_idivl_EAX_T0(); +} + +#ifdef TARGET_X86_64 +void OPPROTO op_divq_EAX_T0(void) +{ + helper_divq_EAX_T0(); +} + +void OPPROTO op_idivq_EAX_T0(void) +{ + helper_idivq_EAX_T0(); +} +#endif + +/* constant load & misc op */ + +/* XXX: consistent names */ +void OPPROTO op_movl_T0_imu(void) +{ + T0 = (uint32_t)PARAM1; +} + +void OPPROTO op_movl_T0_im(void) +{ + T0 = (int32_t)PARAM1; +} + +void OPPROTO op_addl_T0_im(void) +{ + T0 += PARAM1; +} + +void OPPROTO op_andl_T0_ffff(void) +{ + T0 = T0 & 0xffff; +} + +void OPPROTO op_andl_T0_im(void) +{ + T0 = T0 & PARAM1; +} + +void OPPROTO op_movl_T0_T1(void) +{ + T0 = T1; +} + +void OPPROTO op_movl_T1_imu(void) +{ + T1 = (uint32_t)PARAM1; +} + +void OPPROTO op_movl_T1_im(void) +{ + T1 = (int32_t)PARAM1; +} + +void OPPROTO op_addl_T1_im(void) +{ + T1 += PARAM1; +} + +void OPPROTO op_movl_T1_A0(void) +{ + T1 = A0; +} + +void OPPROTO op_movl_A0_im(void) +{ + A0 = (uint32_t)PARAM1; +} + +void OPPROTO op_addl_A0_im(void) +{ + A0 = (uint32_t)(A0 + PARAM1); +} + +void OPPROTO op_movl_A0_seg(void) +{ + A0 = (uint32_t)*(target_ulong *)((char *)env + PARAM1); +} + +void OPPROTO op_addl_A0_seg(void) +{ + A0 = (uint32_t)(A0 + *(target_ulong *)((char *)env + PARAM1)); +} + +void OPPROTO op_addl_A0_AL(void) +{ + A0 = (uint32_t)(A0 + (EAX & 0xff)); +} + +#ifdef WORDS_BIGENDIAN +typedef union UREG64 { + struct { uint16_t v3, v2, v1, v0; } w; + struct { uint32_t v1, v0; } l; + uint64_t q; +} UREG64; +#else +typedef union UREG64 { + struct { uint16_t v0, v1, v2, v3; } w; + struct { uint32_t v0, v1; } l; + uint64_t q; +} UREG64; +#endif + +#ifdef TARGET_X86_64 + +#define PARAMQ1 \ +({\ + UREG64 __p;\ + __p.l.v1 = PARAM1;\ + __p.l.v0 = PARAM2;\ + __p.q;\ +}) + +void OPPROTO op_movq_T0_im64(void) +{ + T0 = PARAMQ1; +} + +void OPPROTO op_movq_T1_im64(void) +{ + T1 = PARAMQ1; +} + +void OPPROTO op_movq_A0_im(void) +{ + A0 = (int32_t)PARAM1; +} + +void OPPROTO op_movq_A0_im64(void) +{ + A0 = PARAMQ1; +} + +void OPPROTO op_addq_A0_im(void) +{ + A0 = (A0 + (int32_t)PARAM1); +} + +void OPPROTO op_addq_A0_im64(void) +{ + A0 = (A0 + PARAMQ1); +} + +void OPPROTO op_movq_A0_seg(void) +{ + A0 = *(target_ulong *)((char *)env + PARAM1); +} + +void OPPROTO op_addq_A0_seg(void) +{ + A0 += *(target_ulong *)((char *)env + PARAM1); +} + +void OPPROTO op_addq_A0_AL(void) +{ + A0 = (A0 + (EAX & 0xff)); +} + +#endif + +void OPPROTO op_andl_A0_ffff(void) +{ + A0 = A0 & 0xffff; +} + +/* memory access */ + +#define MEMSUFFIX _raw +#include "ops_mem.h" + +#if !defined(CONFIG_USER_ONLY) +#define MEMSUFFIX _kernel +#include "ops_mem.h" + +#define MEMSUFFIX _user +#include "ops_mem.h" +#endif + +/* indirect jump */ + +void OPPROTO op_jmp_T0(void) +{ + EIP = T0; +} + +void OPPROTO op_movl_eip_im(void) +{ + EIP = (uint32_t)PARAM1; +} + +#ifdef TARGET_X86_64 +void OPPROTO op_movq_eip_im(void) +{ + EIP = (int32_t)PARAM1; +} + +void OPPROTO op_movq_eip_im64(void) +{ + EIP = PARAMQ1; +} +#endif + +void OPPROTO op_hlt(void) +{ + env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */ + env->hflags |= HF_HALTED_MASK; + env->exception_index = EXCP_HLT; + cpu_loop_exit(); +} + +void OPPROTO op_debug(void) +{ + env->exception_index = EXCP_DEBUG; + cpu_loop_exit(); +} + +void OPPROTO op_raise_interrupt(void) +{ + int intno, next_eip_addend; + intno = PARAM1; + next_eip_addend = PARAM2; + raise_interrupt(intno, 1, 0, next_eip_addend); +} + +void OPPROTO op_raise_exception(void) +{ + int exception_index; + exception_index = PARAM1; + raise_exception(exception_index); +} + +void OPPROTO op_into(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + if (eflags & CC_O) { + raise_interrupt(EXCP04_INTO, 1, 0, PARAM1); + } + FORCE_RET(); +} + +void OPPROTO op_cli(void) +{ + env->eflags &= ~IF_MASK; +} + +void OPPROTO op_sti(void) +{ + env->eflags |= IF_MASK; +} + +void OPPROTO op_set_inhibit_irq(void) +{ + env->hflags |= HF_INHIBIT_IRQ_MASK; +} + +void OPPROTO op_reset_inhibit_irq(void) +{ + env->hflags &= ~HF_INHIBIT_IRQ_MASK; +} + +#if 0 +/* vm86plus instructions */ +void OPPROTO op_cli_vm(void) +{ + env->eflags &= ~VIF_MASK; +} + +void OPPROTO op_sti_vm(void) +{ + env->eflags |= VIF_MASK; + if (env->eflags & VIP_MASK) { + EIP = PARAM1; + raise_exception(EXCP0D_GPF); + } + FORCE_RET(); +} +#endif + +void OPPROTO op_boundw(void) +{ + int low, high, v; + low = ldsw(A0); + high = ldsw(A0 + 2); + v = (int16_t)T0; + if (v < low || v > high) { + raise_exception(EXCP05_BOUND); + } + FORCE_RET(); +} + +void OPPROTO op_boundl(void) +{ + int low, high, v; + low = ldl(A0); + high = ldl(A0 + 4); + v = T0; + if (v < low || v > high) { + raise_exception(EXCP05_BOUND); + } + FORCE_RET(); +} + +void OPPROTO op_cmpxchg8b(void) +{ + helper_cmpxchg8b(); +} + +void OPPROTO op_movl_T0_0(void) +{ + T0 = 0; +} + +void OPPROTO op_exit_tb(void) +{ + EXIT_TB(); +} + +/* multiple size ops */ + +#define ldul ldl + +#define SHIFT 0 +#include "ops_template.h" +#undef SHIFT + +#define SHIFT 1 +#include "ops_template.h" +#undef SHIFT + +#define SHIFT 2 +#include "ops_template.h" +#undef SHIFT + +#ifdef TARGET_X86_64 + +#define SHIFT 3 +#include "ops_template.h" +#undef SHIFT + +#endif + +/* sign extend */ + +void OPPROTO op_movsbl_T0_T0(void) +{ + T0 = (int8_t)T0; +} + +void OPPROTO op_movzbl_T0_T0(void) +{ + T0 = (uint8_t)T0; +} + +void OPPROTO op_movswl_T0_T0(void) +{ + T0 = (int16_t)T0; +} + +void OPPROTO op_movzwl_T0_T0(void) +{ + T0 = (uint16_t)T0; +} + +void OPPROTO op_movswl_EAX_AX(void) +{ + EAX = (int16_t)EAX; +} + +#ifdef TARGET_X86_64 +void OPPROTO op_movslq_T0_T0(void) +{ + T0 = (int32_t)T0; +} + +void OPPROTO op_movslq_RAX_EAX(void) +{ + EAX = (int32_t)EAX; +} +#endif + +void OPPROTO op_movsbw_AX_AL(void) +{ + EAX = (EAX & ~0xffff) | ((int8_t)EAX & 0xffff); +} + +void OPPROTO op_movslq_EDX_EAX(void) +{ + EDX = (int32_t)EAX >> 31; +} + +void OPPROTO op_movswl_DX_AX(void) +{ + EDX = (EDX & ~0xffff) | (((int16_t)EAX >> 15) & 0xffff); +} + +#ifdef TARGET_X86_64 +void OPPROTO op_movsqo_RDX_RAX(void) +{ + EDX = (int64_t)EAX >> 63; +} +#endif + +/* string ops helpers */ + +void OPPROTO op_addl_ESI_T0(void) +{ + ESI = (uint32_t)(ESI + T0); +} + +void OPPROTO op_addw_ESI_T0(void) +{ + ESI = (ESI & ~0xffff) | ((ESI + T0) & 0xffff); +} + +void OPPROTO op_addl_EDI_T0(void) +{ + EDI = (uint32_t)(EDI + T0); +} + +void OPPROTO op_addw_EDI_T0(void) +{ + EDI = (EDI & ~0xffff) | ((EDI + T0) & 0xffff); +} + +void OPPROTO op_decl_ECX(void) +{ + ECX = (uint32_t)(ECX - 1); +} + +void OPPROTO op_decw_ECX(void) +{ + ECX = (ECX & ~0xffff) | ((ECX - 1) & 0xffff); +} + +#ifdef TARGET_X86_64 +void OPPROTO op_addq_ESI_T0(void) +{ + ESI = (ESI + T0); +} + +void OPPROTO op_addq_EDI_T0(void) +{ + EDI = (EDI + T0); +} + +void OPPROTO op_decq_ECX(void) +{ + ECX--; +} +#endif + +/* push/pop utils */ + +void op_addl_A0_SS(void) +{ + A0 = (uint32_t)(A0 + env->segs[R_SS].base); +} + +void op_subl_A0_2(void) +{ + A0 = (uint32_t)(A0 - 2); +} + +void op_subl_A0_4(void) +{ + A0 = (uint32_t)(A0 - 4); +} + +void op_addl_ESP_4(void) +{ + ESP = (uint32_t)(ESP + 4); +} + +void op_addl_ESP_2(void) +{ + ESP = (uint32_t)(ESP + 2); +} + +void op_addw_ESP_4(void) +{ + ESP = (ESP & ~0xffff) | ((ESP + 4) & 0xffff); +} + +void op_addw_ESP_2(void) +{ + ESP = (ESP & ~0xffff) | ((ESP + 2) & 0xffff); +} + +void op_addl_ESP_im(void) +{ + ESP = (uint32_t)(ESP + PARAM1); +} + +void op_addw_ESP_im(void) +{ + ESP = (ESP & ~0xffff) | ((ESP + PARAM1) & 0xffff); +} + +#ifdef TARGET_X86_64 +void op_subq_A0_2(void) +{ + A0 -= 2; +} + +void op_subq_A0_8(void) +{ + A0 -= 8; +} + +void op_addq_ESP_8(void) +{ + ESP += 8; +} + +void op_addq_ESP_im(void) +{ + ESP += PARAM1; +} +#endif + +void OPPROTO op_rdtsc(void) +{ + helper_rdtsc(); +} + +void OPPROTO op_cpuid(void) +{ + helper_cpuid(); +} + +void OPPROTO op_enter_level(void) +{ + helper_enter_level(PARAM1, PARAM2); +} + +#ifdef TARGET_X86_64 +void OPPROTO op_enter64_level(void) +{ + helper_enter64_level(PARAM1, PARAM2); +} +#endif + +void OPPROTO op_sysenter(void) +{ + helper_sysenter(); +} + +void OPPROTO op_sysexit(void) +{ + helper_sysexit(); +} + +#ifdef TARGET_X86_64 +void OPPROTO op_syscall(void) +{ + helper_syscall(PARAM1); +} + +void OPPROTO op_sysret(void) +{ + helper_sysret(PARAM1); +} +#endif + +void OPPROTO op_rdmsr(void) +{ + helper_rdmsr(); +} + +void OPPROTO op_wrmsr(void) +{ + helper_wrmsr(); +} + +/* bcd */ + +/* XXX: exception */ +void OPPROTO op_aam(void) +{ + int base = PARAM1; + int al, ah; + al = EAX & 0xff; + ah = al / base; + al = al % base; + EAX = (EAX & ~0xffff) | al | (ah << 8); + CC_DST = al; +} + +void OPPROTO op_aad(void) +{ + int base = PARAM1; + int al, ah; + al = EAX & 0xff; + ah = (EAX >> 8) & 0xff; + al = ((ah * base) + al) & 0xff; + EAX = (EAX & ~0xffff) | al; + CC_DST = al; +} + +void OPPROTO op_aaa(void) +{ + int icarry; + int al, ah, af; + int eflags; + + eflags = cc_table[CC_OP].compute_all(); + af = eflags & CC_A; + al = EAX & 0xff; + ah = (EAX >> 8) & 0xff; + + icarry = (al > 0xf9); + if (((al & 0x0f) > 9 ) || af) { + al = (al + 6) & 0x0f; + ah = (ah + 1 + icarry) & 0xff; + eflags |= CC_C | CC_A; + } else { + eflags &= ~(CC_C | CC_A); + al &= 0x0f; + } + EAX = (EAX & ~0xffff) | al | (ah << 8); + CC_SRC = eflags; + FORCE_RET(); +} + +void OPPROTO op_aas(void) +{ + int icarry; + int al, ah, af; + int eflags; + + eflags = cc_table[CC_OP].compute_all(); + af = eflags & CC_A; + al = EAX & 0xff; + ah = (EAX >> 8) & 0xff; + + icarry = (al < 6); + if (((al & 0x0f) > 9 ) || af) { + al = (al - 6) & 0x0f; + ah = (ah - 1 - icarry) & 0xff; + eflags |= CC_C | CC_A; + } else { + eflags &= ~(CC_C | CC_A); + al &= 0x0f; + } + EAX = (EAX & ~0xffff) | al | (ah << 8); + CC_SRC = eflags; + FORCE_RET(); +} + +void OPPROTO op_daa(void) +{ + int al, af, cf; + int eflags; + + eflags = cc_table[CC_OP].compute_all(); + cf = eflags & CC_C; + af = eflags & CC_A; + al = EAX & 0xff; + + eflags = 0; + if (((al & 0x0f) > 9 ) || af) { + al = (al + 6) & 0xff; + eflags |= CC_A; + } + if ((al > 0x9f) || cf) { + al = (al + 0x60) & 0xff; + eflags |= CC_C; + } + EAX = (EAX & ~0xff) | al; + /* well, speed is not an issue here, so we compute the flags by hand */ + eflags |= (al == 0) << 6; /* zf */ + eflags |= parity_table[al]; /* pf */ + eflags |= (al & 0x80); /* sf */ + CC_SRC = eflags; + FORCE_RET(); +} + +void OPPROTO op_das(void) +{ + int al, al1, af, cf; + int eflags; + + eflags = cc_table[CC_OP].compute_all(); + cf = eflags & CC_C; + af = eflags & CC_A; + al = EAX & 0xff; + + eflags = 0; + al1 = al; + if (((al & 0x0f) > 9 ) || af) { + eflags |= CC_A; + if (al < 6 || cf) + eflags |= CC_C; + al = (al - 6) & 0xff; + } + if ((al1 > 0x99) || cf) { + al = (al - 0x60) & 0xff; + eflags |= CC_C; + } + EAX = (EAX & ~0xff) | al; + /* well, speed is not an issue here, so we compute the flags by hand */ + eflags |= (al == 0) << 6; /* zf */ + eflags |= parity_table[al]; /* pf */ + eflags |= (al & 0x80); /* sf */ + CC_SRC = eflags; + FORCE_RET(); +} + +/* segment handling */ + +/* never use it with R_CS */ +void OPPROTO op_movl_seg_T0(void) +{ + load_seg(PARAM1, T0); +} + +/* faster VM86 version */ +void OPPROTO op_movl_seg_T0_vm(void) +{ + int selector; + SegmentCache *sc; + + selector = T0 & 0xffff; + /* env->segs[] access */ + sc = (SegmentCache *)((char *)env + PARAM1); + sc->selector = selector; + sc->base = (selector << 4); +} + +void OPPROTO op_movl_T0_seg(void) +{ + T0 = env->segs[PARAM1].selector; +} + +void OPPROTO op_lsl(void) +{ + helper_lsl(); +} + +void OPPROTO op_lar(void) +{ + helper_lar(); +} + +void OPPROTO op_verr(void) +{ + helper_verr(); +} + +void OPPROTO op_verw(void) +{ + helper_verw(); +} + +void OPPROTO op_arpl(void) +{ + if ((T0 & 3) < (T1 & 3)) { + /* XXX: emulate bug or 0xff3f0000 oring as in bochs ? */ + T0 = (T0 & ~3) | (T1 & 3); + T1 = CC_Z; + } else { + T1 = 0; + } + FORCE_RET(); +} + +void OPPROTO op_arpl_update(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + CC_SRC = (eflags & ~CC_Z) | T1; +} + +/* T0: segment, T1:eip */ +void OPPROTO op_ljmp_protected_T0_T1(void) +{ + helper_ljmp_protected_T0_T1(PARAM1); +} + +void OPPROTO op_lcall_real_T0_T1(void) +{ + helper_lcall_real_T0_T1(PARAM1, PARAM2); +} + +void OPPROTO op_lcall_protected_T0_T1(void) +{ + helper_lcall_protected_T0_T1(PARAM1, PARAM2); +} + +void OPPROTO op_iret_real(void) +{ + helper_iret_real(PARAM1); +} + +void OPPROTO op_iret_protected(void) +{ + helper_iret_protected(PARAM1, PARAM2); +} + +void OPPROTO op_lret_protected(void) +{ + helper_lret_protected(PARAM1, PARAM2); +} + +void OPPROTO op_lldt_T0(void) +{ + helper_lldt_T0(); +} + +void OPPROTO op_ltr_T0(void) +{ + helper_ltr_T0(); +} + +/* CR registers access */ +void OPPROTO op_movl_crN_T0(void) +{ + helper_movl_crN_T0(PARAM1); +} + +#if !defined(CONFIG_USER_ONLY) +void OPPROTO op_movtl_T0_cr8(void) +{ + T0 = cpu_get_apic_tpr(env); +} +#endif + +/* DR registers access */ +void OPPROTO op_movl_drN_T0(void) +{ + helper_movl_drN_T0(PARAM1); +} + +void OPPROTO op_lmsw_T0(void) +{ + /* only 4 lower bits of CR0 are modified. PE cannot be set to zero + if already set to one. */ + T0 = (env->cr[0] & ~0xe) | (T0 & 0xf); + helper_movl_crN_T0(0); +} + +void OPPROTO op_invlpg_A0(void) +{ + helper_invlpg(A0); +} + +void OPPROTO op_movl_T0_env(void) +{ + T0 = *(uint32_t *)((char *)env + PARAM1); +} + +void OPPROTO op_movl_env_T0(void) +{ + *(uint32_t *)((char *)env + PARAM1) = T0; +} + +void OPPROTO op_movl_env_T1(void) +{ + *(uint32_t *)((char *)env + PARAM1) = T1; +} + +void OPPROTO op_movtl_T0_env(void) +{ + T0 = *(target_ulong *)((char *)env + PARAM1); +} + +void OPPROTO op_movtl_env_T0(void) +{ + *(target_ulong *)((char *)env + PARAM1) = T0; +} + +void OPPROTO op_movtl_T1_env(void) +{ + T1 = *(target_ulong *)((char *)env + PARAM1); +} + +void OPPROTO op_movtl_env_T1(void) +{ + *(target_ulong *)((char *)env + PARAM1) = T1; +} + +void OPPROTO op_clts(void) +{ + env->cr[0] &= ~CR0_TS_MASK; + env->hflags &= ~HF_TS_MASK; +} + +/* flags handling */ + +void OPPROTO op_goto_tb0(void) +{ + GOTO_TB(op_goto_tb0, PARAM1, 0); +} + +void OPPROTO op_goto_tb1(void) +{ + GOTO_TB(op_goto_tb1, PARAM1, 1); +} + +void OPPROTO op_jmp_label(void) +{ + GOTO_LABEL_PARAM(1); +} + +void OPPROTO op_jnz_T0_label(void) +{ + if (T0) + GOTO_LABEL_PARAM(1); + FORCE_RET(); +} + +void OPPROTO op_jz_T0_label(void) +{ + if (!T0) + GOTO_LABEL_PARAM(1); + FORCE_RET(); +} + +/* slow set cases (compute x86 flags) */ +void OPPROTO op_seto_T0_cc(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + T0 = (eflags >> 11) & 1; +} + +void OPPROTO op_setb_T0_cc(void) +{ + T0 = cc_table[CC_OP].compute_c(); +} + +void OPPROTO op_setz_T0_cc(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + T0 = (eflags >> 6) & 1; +} + +void OPPROTO op_setbe_T0_cc(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + T0 = (eflags & (CC_Z | CC_C)) != 0; +} + +void OPPROTO op_sets_T0_cc(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + T0 = (eflags >> 7) & 1; +} + +void OPPROTO op_setp_T0_cc(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + T0 = (eflags >> 2) & 1; +} + +void OPPROTO op_setl_T0_cc(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + T0 = ((eflags ^ (eflags >> 4)) >> 7) & 1; +} + +void OPPROTO op_setle_T0_cc(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + T0 = (((eflags ^ (eflags >> 4)) & 0x80) || (eflags & CC_Z)) != 0; +} + +void OPPROTO op_xor_T0_1(void) +{ + T0 ^= 1; +} + +void OPPROTO op_set_cc_op(void) +{ + CC_OP = PARAM1; +} + +void OPPROTO op_mov_T0_cc(void) +{ + T0 = cc_table[CC_OP].compute_all(); +} + +/* XXX: clear VIF/VIP in all ops ? */ + +void OPPROTO op_movl_eflags_T0(void) +{ + load_eflags(T0, (TF_MASK | AC_MASK | ID_MASK | NT_MASK)); +} + +void OPPROTO op_movw_eflags_T0(void) +{ + load_eflags(T0, (TF_MASK | AC_MASK | ID_MASK | NT_MASK) & 0xffff); +} + +void OPPROTO op_movl_eflags_T0_io(void) +{ + load_eflags(T0, (TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK)); +} + +void OPPROTO op_movw_eflags_T0_io(void) +{ + load_eflags(T0, (TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK) & 0xffff); +} + +void OPPROTO op_movl_eflags_T0_cpl0(void) +{ + load_eflags(T0, (TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK | IOPL_MASK)); +} + +void OPPROTO op_movw_eflags_T0_cpl0(void) +{ + load_eflags(T0, (TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK | IOPL_MASK) & 0xffff); +} + +#if 0 +/* vm86plus version */ +void OPPROTO op_movw_eflags_T0_vm(void) +{ + int eflags; + eflags = T0; + CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + DF = 1 - (2 * ((eflags >> 10) & 1)); + /* we also update some system flags as in user mode */ + env->eflags = (env->eflags & ~(FL_UPDATE_MASK16 | VIF_MASK)) | + (eflags & FL_UPDATE_MASK16); + if (eflags & IF_MASK) { + env->eflags |= VIF_MASK; + if (env->eflags & VIP_MASK) { + EIP = PARAM1; + raise_exception(EXCP0D_GPF); + } + } + FORCE_RET(); +} + +void OPPROTO op_movl_eflags_T0_vm(void) +{ + int eflags; + eflags = T0; + CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + DF = 1 - (2 * ((eflags >> 10) & 1)); + /* we also update some system flags as in user mode */ + env->eflags = (env->eflags & ~(FL_UPDATE_MASK32 | VIF_MASK)) | + (eflags & FL_UPDATE_MASK32); + if (eflags & IF_MASK) { + env->eflags |= VIF_MASK; + if (env->eflags & VIP_MASK) { + EIP = PARAM1; + raise_exception(EXCP0D_GPF); + } + } + FORCE_RET(); +} +#endif + +/* XXX: compute only O flag */ +void OPPROTO op_movb_eflags_T0(void) +{ + int of; + of = cc_table[CC_OP].compute_all() & CC_O; + CC_SRC = (T0 & (CC_S | CC_Z | CC_A | CC_P | CC_C)) | of; +} + +void OPPROTO op_movl_T0_eflags(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + eflags |= (DF & DF_MASK); + eflags |= env->eflags & ~(VM_MASK | RF_MASK); + T0 = eflags; +} + +/* vm86plus version */ +#if 0 +void OPPROTO op_movl_T0_eflags_vm(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + eflags |= (DF & DF_MASK); + eflags |= env->eflags & ~(VM_MASK | RF_MASK | IF_MASK); + if (env->eflags & VIF_MASK) + eflags |= IF_MASK; + T0 = eflags; +} +#endif + +void OPPROTO op_cld(void) +{ + DF = 1; +} + +void OPPROTO op_std(void) +{ + DF = -1; +} + +void OPPROTO op_clc(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + eflags &= ~CC_C; + CC_SRC = eflags; +} + +void OPPROTO op_stc(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + eflags |= CC_C; + CC_SRC = eflags; +} + +void OPPROTO op_cmc(void) +{ + int eflags; + eflags = cc_table[CC_OP].compute_all(); + eflags ^= CC_C; + CC_SRC = eflags; +} + +void OPPROTO op_salc(void) +{ + int cf; + cf = cc_table[CC_OP].compute_c(); + EAX = (EAX & ~0xff) | ((-cf) & 0xff); +} + +static int compute_all_eflags(void) +{ + return CC_SRC; +} + +static int compute_c_eflags(void) +{ + return CC_SRC & CC_C; +} + +CCTable cc_table[CC_OP_NB] = { + [CC_OP_DYNAMIC] = { /* should never happen */ }, + + [CC_OP_EFLAGS] = { compute_all_eflags, compute_c_eflags }, + + [CC_OP_MULB] = { compute_all_mulb, compute_c_mull }, + [CC_OP_MULW] = { compute_all_mulw, compute_c_mull }, + [CC_OP_MULL] = { compute_all_mull, compute_c_mull }, + + [CC_OP_ADDB] = { compute_all_addb, compute_c_addb }, + [CC_OP_ADDW] = { compute_all_addw, compute_c_addw }, + [CC_OP_ADDL] = { compute_all_addl, compute_c_addl }, + + [CC_OP_ADCB] = { compute_all_adcb, compute_c_adcb }, + [CC_OP_ADCW] = { compute_all_adcw, compute_c_adcw }, + [CC_OP_ADCL] = { compute_all_adcl, compute_c_adcl }, + + [CC_OP_SUBB] = { compute_all_subb, compute_c_subb }, + [CC_OP_SUBW] = { compute_all_subw, compute_c_subw }, + [CC_OP_SUBL] = { compute_all_subl, compute_c_subl }, + + [CC_OP_SBBB] = { compute_all_sbbb, compute_c_sbbb }, + [CC_OP_SBBW] = { compute_all_sbbw, compute_c_sbbw }, + [CC_OP_SBBL] = { compute_all_sbbl, compute_c_sbbl }, + + [CC_OP_LOGICB] = { compute_all_logicb, compute_c_logicb }, + [CC_OP_LOGICW] = { compute_all_logicw, compute_c_logicw }, + [CC_OP_LOGICL] = { compute_all_logicl, compute_c_logicl }, + + [CC_OP_INCB] = { compute_all_incb, compute_c_incl }, + [CC_OP_INCW] = { compute_all_incw, compute_c_incl }, + [CC_OP_INCL] = { compute_all_incl, compute_c_incl }, + + [CC_OP_DECB] = { compute_all_decb, compute_c_incl }, + [CC_OP_DECW] = { compute_all_decw, compute_c_incl }, + [CC_OP_DECL] = { compute_all_decl, compute_c_incl }, + + [CC_OP_SHLB] = { compute_all_shlb, compute_c_shlb }, + [CC_OP_SHLW] = { compute_all_shlw, compute_c_shlw }, + [CC_OP_SHLL] = { compute_all_shll, compute_c_shll }, + + [CC_OP_SARB] = { compute_all_sarb, compute_c_sarl }, + [CC_OP_SARW] = { compute_all_sarw, compute_c_sarl }, + [CC_OP_SARL] = { compute_all_sarl, compute_c_sarl }, + +#ifdef TARGET_X86_64 + [CC_OP_MULQ] = { compute_all_mulq, compute_c_mull }, + + [CC_OP_ADDQ] = { compute_all_addq, compute_c_addq }, + + [CC_OP_ADCQ] = { compute_all_adcq, compute_c_adcq }, + + [CC_OP_SUBQ] = { compute_all_subq, compute_c_subq }, + + [CC_OP_SBBQ] = { compute_all_sbbq, compute_c_sbbq }, + + [CC_OP_LOGICQ] = { compute_all_logicq, compute_c_logicq }, + + [CC_OP_INCQ] = { compute_all_incq, compute_c_incl }, + + [CC_OP_DECQ] = { compute_all_decq, compute_c_incl }, + + [CC_OP_SHLQ] = { compute_all_shlq, compute_c_shlq }, + + [CC_OP_SARQ] = { compute_all_sarq, compute_c_sarl }, +#endif +}; + +/* floating point support. Some of the code for complicated x87 + functions comes from the LGPL'ed x86 emulator found in the Willows + TWIN windows emulator. */ + +/* fp load FT0 */ + +void OPPROTO op_flds_FT0_A0(void) +{ +#ifdef USE_FP_CONVERT + FP_CONVERT.i32 = ldl(A0); + FT0 = FP_CONVERT.f; +#else + FT0 = ldfl(A0); +#endif +} + +void OPPROTO op_fldl_FT0_A0(void) +{ +#ifdef USE_FP_CONVERT + FP_CONVERT.i64 = ldq(A0); + FT0 = FP_CONVERT.d; +#else + FT0 = ldfq(A0); +#endif +} + +/* helpers are needed to avoid static constant reference. XXX: find a better way */ +#ifdef USE_INT_TO_FLOAT_HELPERS + +void helper_fild_FT0_A0(void) +{ + FT0 = (CPU86_LDouble)ldsw(A0); +} + +void helper_fildl_FT0_A0(void) +{ + FT0 = (CPU86_LDouble)((int32_t)ldl(A0)); +} + +void helper_fildll_FT0_A0(void) +{ + FT0 = (CPU86_LDouble)((int64_t)ldq(A0)); +} + +void OPPROTO op_fild_FT0_A0(void) +{ + helper_fild_FT0_A0(); +} + +void OPPROTO op_fildl_FT0_A0(void) +{ + helper_fildl_FT0_A0(); +} + +void OPPROTO op_fildll_FT0_A0(void) +{ + helper_fildll_FT0_A0(); +} + +#else + +void OPPROTO op_fild_FT0_A0(void) +{ +#ifdef USE_FP_CONVERT + FP_CONVERT.i32 = ldsw(A0); + FT0 = (CPU86_LDouble)FP_CONVERT.i32; +#else + FT0 = (CPU86_LDouble)ldsw(A0); +#endif +} + +void OPPROTO op_fildl_FT0_A0(void) +{ +#ifdef USE_FP_CONVERT + FP_CONVERT.i32 = (int32_t) ldl(A0); + FT0 = (CPU86_LDouble)FP_CONVERT.i32; +#else + FT0 = (CPU86_LDouble)((int32_t)ldl(A0)); +#endif +} + +void OPPROTO op_fildll_FT0_A0(void) +{ +#ifdef USE_FP_CONVERT + FP_CONVERT.i64 = (int64_t) ldq(A0); + FT0 = (CPU86_LDouble)FP_CONVERT.i64; +#else + FT0 = (CPU86_LDouble)((int64_t)ldq(A0)); +#endif +} +#endif + +/* fp load ST0 */ + +void OPPROTO op_flds_ST0_A0(void) +{ + int new_fpstt; + new_fpstt = (env->fpstt - 1) & 7; +#ifdef USE_FP_CONVERT + FP_CONVERT.i32 = ldl(A0); + env->fpregs[new_fpstt].d = FP_CONVERT.f; +#else + env->fpregs[new_fpstt].d = ldfl(A0); +#endif + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void OPPROTO op_fldl_ST0_A0(void) +{ + int new_fpstt; + new_fpstt = (env->fpstt - 1) & 7; +#ifdef USE_FP_CONVERT + FP_CONVERT.i64 = ldq(A0); + env->fpregs[new_fpstt].d = FP_CONVERT.d; +#else + env->fpregs[new_fpstt].d = ldfq(A0); +#endif + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void OPPROTO op_fldt_ST0_A0(void) +{ + helper_fldt_ST0_A0(); +} + +/* helpers are needed to avoid static constant reference. XXX: find a better way */ +#ifdef USE_INT_TO_FLOAT_HELPERS + +void helper_fild_ST0_A0(void) +{ + int new_fpstt; + new_fpstt = (env->fpstt - 1) & 7; + env->fpregs[new_fpstt].d = (CPU86_LDouble)ldsw(A0); + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void helper_fildl_ST0_A0(void) +{ + int new_fpstt; + new_fpstt = (env->fpstt - 1) & 7; + env->fpregs[new_fpstt].d = (CPU86_LDouble)((int32_t)ldl(A0)); + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void helper_fildll_ST0_A0(void) +{ + int new_fpstt; + new_fpstt = (env->fpstt - 1) & 7; + env->fpregs[new_fpstt].d = (CPU86_LDouble)((int64_t)ldq(A0)); + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void OPPROTO op_fild_ST0_A0(void) +{ + helper_fild_ST0_A0(); +} + +void OPPROTO op_fildl_ST0_A0(void) +{ + helper_fildl_ST0_A0(); +} + +void OPPROTO op_fildll_ST0_A0(void) +{ + helper_fildll_ST0_A0(); +} + +#else + +void OPPROTO op_fild_ST0_A0(void) +{ + int new_fpstt; + new_fpstt = (env->fpstt - 1) & 7; +#ifdef USE_FP_CONVERT + FP_CONVERT.i32 = ldsw(A0); + env->fpregs[new_fpstt].d = (CPU86_LDouble)FP_CONVERT.i32; +#else + env->fpregs[new_fpstt].d = (CPU86_LDouble)ldsw(A0); +#endif + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void OPPROTO op_fildl_ST0_A0(void) +{ + int new_fpstt; + new_fpstt = (env->fpstt - 1) & 7; +#ifdef USE_FP_CONVERT + FP_CONVERT.i32 = (int32_t) ldl(A0); + env->fpregs[new_fpstt].d = (CPU86_LDouble)FP_CONVERT.i32; +#else + env->fpregs[new_fpstt].d = (CPU86_LDouble)((int32_t)ldl(A0)); +#endif + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void OPPROTO op_fildll_ST0_A0(void) +{ + int new_fpstt; + new_fpstt = (env->fpstt - 1) & 7; +#ifdef USE_FP_CONVERT + FP_CONVERT.i64 = (int64_t) ldq(A0); + env->fpregs[new_fpstt].d = (CPU86_LDouble)FP_CONVERT.i64; +#else + env->fpregs[new_fpstt].d = (CPU86_LDouble)((int64_t)ldq(A0)); +#endif + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +#endif + +/* fp store */ + +void OPPROTO op_fsts_ST0_A0(void) +{ +#ifdef USE_FP_CONVERT + FP_CONVERT.f = (float)ST0; + stfl(A0, FP_CONVERT.f); +#else + stfl(A0, (float)ST0); +#endif + FORCE_RET(); +} + +void OPPROTO op_fstl_ST0_A0(void) +{ + stfq(A0, (double)ST0); + FORCE_RET(); +} + +void OPPROTO op_fstt_ST0_A0(void) +{ + helper_fstt_ST0_A0(); +} + +void OPPROTO op_fist_ST0_A0(void) +{ +#if defined(__sparc__) && !defined(__sparc_v9__) + register CPU86_LDouble d asm("o0"); +#else + CPU86_LDouble d; +#endif + int val; + + d = ST0; + val = floatx_to_int32(d, &env->fp_status); + if (val != (int16_t)val) + val = -32768; + stw(A0, val); + FORCE_RET(); +} + +void OPPROTO op_fistl_ST0_A0(void) +{ +#if defined(__sparc__) && !defined(__sparc_v9__) + register CPU86_LDouble d asm("o0"); +#else + CPU86_LDouble d; +#endif + int val; + + d = ST0; + val = floatx_to_int32(d, &env->fp_status); + stl(A0, val); + FORCE_RET(); +} + +void OPPROTO op_fistll_ST0_A0(void) +{ +#if defined(__sparc__) && !defined(__sparc_v9__) + register CPU86_LDouble d asm("o0"); +#else + CPU86_LDouble d; +#endif + int64_t val; + + d = ST0; + val = floatx_to_int64(d, &env->fp_status); + stq(A0, val); + FORCE_RET(); +} + +void OPPROTO op_fistt_ST0_A0(void) +{ +#if defined(__sparc__) && !defined(__sparc_v9__) + register CPU86_LDouble d asm("o0"); +#else + CPU86_LDouble d; +#endif + int val; + + d = ST0; + val = floatx_to_int32_round_to_zero(d, &env->fp_status); + if (val != (int16_t)val) + val = -32768; + stw(A0, val); + FORCE_RET(); +} + +void OPPROTO op_fisttl_ST0_A0(void) +{ +#if defined(__sparc__) && !defined(__sparc_v9__) + register CPU86_LDouble d asm("o0"); +#else + CPU86_LDouble d; +#endif + int val; + + d = ST0; + val = floatx_to_int32_round_to_zero(d, &env->fp_status); + stl(A0, val); + FORCE_RET(); +} + +void OPPROTO op_fisttll_ST0_A0(void) +{ +#if defined(__sparc__) && !defined(__sparc_v9__) + register CPU86_LDouble d asm("o0"); +#else + CPU86_LDouble d; +#endif + int64_t val; + + d = ST0; + val = floatx_to_int64_round_to_zero(d, &env->fp_status); + stq(A0, val); + FORCE_RET(); +} + +void OPPROTO op_fbld_ST0_A0(void) +{ + helper_fbld_ST0_A0(); +} + +void OPPROTO op_fbst_ST0_A0(void) +{ + helper_fbst_ST0_A0(); +} + +/* FPU move */ + +void OPPROTO op_fpush(void) +{ + fpush(); +} + +void OPPROTO op_fpop(void) +{ + fpop(); +} + +void OPPROTO op_fdecstp(void) +{ + env->fpstt = (env->fpstt - 1) & 7; + env->fpus &= (~0x4700); +} + +void OPPROTO op_fincstp(void) +{ + env->fpstt = (env->fpstt + 1) & 7; + env->fpus &= (~0x4700); +} + +void OPPROTO op_ffree_STN(void) +{ + env->fptags[(env->fpstt + PARAM1) & 7] = 1; +} + +void OPPROTO op_fmov_ST0_FT0(void) +{ + ST0 = FT0; +} + +void OPPROTO op_fmov_FT0_STN(void) +{ + FT0 = ST(PARAM1); +} + +void OPPROTO op_fmov_ST0_STN(void) +{ + ST0 = ST(PARAM1); +} + +void OPPROTO op_fmov_STN_ST0(void) +{ + ST(PARAM1) = ST0; +} + +void OPPROTO op_fxchg_ST0_STN(void) +{ + CPU86_LDouble tmp; + tmp = ST(PARAM1); + ST(PARAM1) = ST0; + ST0 = tmp; +} + +/* FPU operations */ + +const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500}; + +void OPPROTO op_fcom_ST0_FT0(void) +{ + int ret; + + ret = floatx_compare(ST0, FT0, &env->fp_status); + env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1]; + FORCE_RET(); +} + +void OPPROTO op_fucom_ST0_FT0(void) +{ + int ret; + + ret = floatx_compare_quiet(ST0, FT0, &env->fp_status); + env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret+ 1]; + FORCE_RET(); +} + +const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C}; + +void OPPROTO op_fcomi_ST0_FT0(void) +{ + int eflags; + int ret; + + ret = floatx_compare(ST0, FT0, &env->fp_status); + eflags = cc_table[CC_OP].compute_all(); + eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1]; + CC_SRC = eflags; + FORCE_RET(); +} + +void OPPROTO op_fucomi_ST0_FT0(void) +{ + int eflags; + int ret; + + ret = floatx_compare_quiet(ST0, FT0, &env->fp_status); + eflags = cc_table[CC_OP].compute_all(); + eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1]; + CC_SRC = eflags; + FORCE_RET(); +} + +void OPPROTO op_fcmov_ST0_STN_T0(void) +{ + if (T0) { + ST0 = ST(PARAM1); + } + FORCE_RET(); +} + +void OPPROTO op_fadd_ST0_FT0(void) +{ + ST0 += FT0; +} + +void OPPROTO op_fmul_ST0_FT0(void) +{ + ST0 *= FT0; +} + +void OPPROTO op_fsub_ST0_FT0(void) +{ + ST0 -= FT0; +} + +void OPPROTO op_fsubr_ST0_FT0(void) +{ + ST0 = FT0 - ST0; +} + +void OPPROTO op_fdiv_ST0_FT0(void) +{ + ST0 = helper_fdiv(ST0, FT0); +} + +void OPPROTO op_fdivr_ST0_FT0(void) +{ + ST0 = helper_fdiv(FT0, ST0); +} + +/* fp operations between STN and ST0 */ + +void OPPROTO op_fadd_STN_ST0(void) +{ + ST(PARAM1) += ST0; +} + +void OPPROTO op_fmul_STN_ST0(void) +{ + ST(PARAM1) *= ST0; +} + +void OPPROTO op_fsub_STN_ST0(void) +{ + ST(PARAM1) -= ST0; +} + +void OPPROTO op_fsubr_STN_ST0(void) +{ + CPU86_LDouble *p; + p = &ST(PARAM1); + *p = ST0 - *p; +} + +void OPPROTO op_fdiv_STN_ST0(void) +{ + CPU86_LDouble *p; + p = &ST(PARAM1); + *p = helper_fdiv(*p, ST0); +} + +void OPPROTO op_fdivr_STN_ST0(void) +{ + CPU86_LDouble *p; + p = &ST(PARAM1); + *p = helper_fdiv(ST0, *p); +} + +/* misc FPU operations */ +void OPPROTO op_fchs_ST0(void) +{ + ST0 = floatx_chs(ST0); +} + +void OPPROTO op_fabs_ST0(void) +{ + ST0 = floatx_abs(ST0); +} + +void OPPROTO op_fxam_ST0(void) +{ + helper_fxam_ST0(); +} + +void OPPROTO op_fld1_ST0(void) +{ + ST0 = f15rk[1]; +} + +void OPPROTO op_fldl2t_ST0(void) +{ + ST0 = f15rk[6]; +} + +void OPPROTO op_fldl2e_ST0(void) +{ + ST0 = f15rk[5]; +} + +void OPPROTO op_fldpi_ST0(void) +{ + ST0 = f15rk[2]; +} + +void OPPROTO op_fldlg2_ST0(void) +{ + ST0 = f15rk[3]; +} + +void OPPROTO op_fldln2_ST0(void) +{ + ST0 = f15rk[4]; +} + +void OPPROTO op_fldz_ST0(void) +{ + ST0 = f15rk[0]; +} + +void OPPROTO op_fldz_FT0(void) +{ + FT0 = f15rk[0]; +} + +/* associated heplers to reduce generated code length and to simplify + relocation (FP constants are usually stored in .rodata section) */ + +void OPPROTO op_f2xm1(void) +{ + helper_f2xm1(); +} + +void OPPROTO op_fyl2x(void) +{ + helper_fyl2x(); +} + +void OPPROTO op_fptan(void) +{ + helper_fptan(); +} + +void OPPROTO op_fpatan(void) +{ + helper_fpatan(); +} + +void OPPROTO op_fxtract(void) +{ + helper_fxtract(); +} + +void OPPROTO op_fprem1(void) +{ + helper_fprem1(); +} + + +void OPPROTO op_fprem(void) +{ + helper_fprem(); +} + +void OPPROTO op_fyl2xp1(void) +{ + helper_fyl2xp1(); +} + +void OPPROTO op_fsqrt(void) +{ + helper_fsqrt(); +} + +void OPPROTO op_fsincos(void) +{ + helper_fsincos(); +} + +void OPPROTO op_frndint(void) +{ + helper_frndint(); +} + +void OPPROTO op_fscale(void) +{ + helper_fscale(); +} + +void OPPROTO op_fsin(void) +{ + helper_fsin(); +} + +void OPPROTO op_fcos(void) +{ + helper_fcos(); +} + +void OPPROTO op_fnstsw_A0(void) +{ + int fpus; + fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + stw(A0, fpus); + FORCE_RET(); +} + +void OPPROTO op_fnstsw_EAX(void) +{ + int fpus; + fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + EAX = (EAX & ~0xffff) | fpus; +} + +void OPPROTO op_fnstcw_A0(void) +{ + stw(A0, env->fpuc); + FORCE_RET(); +} + +void OPPROTO op_fldcw_A0(void) +{ + env->fpuc = lduw(A0); + update_fp_status(); +} + +void OPPROTO op_fclex(void) +{ + env->fpus &= 0x7f00; +} + +void OPPROTO op_fwait(void) +{ + if (env->fpus & FPUS_SE) + fpu_raise_exception(); + FORCE_RET(); +} + +void OPPROTO op_fninit(void) +{ + env->fpus = 0; + env->fpstt = 0; + env->fpuc = 0x37f; + env->fptags[0] = 1; + env->fptags[1] = 1; + env->fptags[2] = 1; + env->fptags[3] = 1; + env->fptags[4] = 1; + env->fptags[5] = 1; + env->fptags[6] = 1; + env->fptags[7] = 1; +} + +void OPPROTO op_fnstenv_A0(void) +{ + helper_fstenv(A0, PARAM1); +} + +void OPPROTO op_fldenv_A0(void) +{ + helper_fldenv(A0, PARAM1); +} + +void OPPROTO op_fnsave_A0(void) +{ + helper_fsave(A0, PARAM1); +} + +void OPPROTO op_frstor_A0(void) +{ + helper_frstor(A0, PARAM1); +} + +/* threading support */ +void OPPROTO op_lock(void) +{ + cpu_lock(); +} + +void OPPROTO op_unlock(void) +{ + cpu_unlock(); +} + +/* SSE support */ +static inline void memcpy16(void *d, void *s) +{ + ((uint32_t *)d)[0] = ((uint32_t *)s)[0]; + ((uint32_t *)d)[1] = ((uint32_t *)s)[1]; + ((uint32_t *)d)[2] = ((uint32_t *)s)[2]; + ((uint32_t *)d)[3] = ((uint32_t *)s)[3]; +} + +void OPPROTO op_movo(void) +{ + /* XXX: badly generated code */ + XMMReg *d, *s; + d = (XMMReg *)((char *)env + PARAM1); + s = (XMMReg *)((char *)env + PARAM2); + memcpy16(d, s); +} + +void OPPROTO op_movq(void) +{ + uint64_t *d, *s; + d = (uint64_t *)((char *)env + PARAM1); + s = (uint64_t *)((char *)env + PARAM2); + *d = *s; +} + +void OPPROTO op_movl(void) +{ + uint32_t *d, *s; + d = (uint32_t *)((char *)env + PARAM1); + s = (uint32_t *)((char *)env + PARAM2); + *d = *s; +} + +void OPPROTO op_movq_env_0(void) +{ + uint64_t *d; + d = (uint64_t *)((char *)env + PARAM1); + *d = 0; +} + +void OPPROTO op_fxsave_A0(void) +{ + helper_fxsave(A0, PARAM1); +} + +void OPPROTO op_fxrstor_A0(void) +{ + helper_fxrstor(A0, PARAM1); +} + +/* XXX: optimize by storing fptt and fptags in the static cpu state */ +void OPPROTO op_enter_mmx(void) +{ + env->fpstt = 0; + *(uint32_t *)(env->fptags) = 0; + *(uint32_t *)(env->fptags + 4) = 0; +} + +void OPPROTO op_emms(void) +{ + /* set to empty state */ + *(uint32_t *)(env->fptags) = 0x01010101; + *(uint32_t *)(env->fptags + 4) = 0x01010101; +} + +#define SHIFT 0 +#include "ops_sse.h" + +#define SHIFT 1 +#include "ops_sse.h" diff --git a/tools/ioemu/target-i386/opreg_template.h b/tools/ioemu/target-i386/opreg_template.h new file mode 100644 index 0000000000..648063650b --- /dev/null +++ b/tools/ioemu/target-i386/opreg_template.h @@ -0,0 +1,190 @@ +/* + * i386 micro operations (templates for various register related + * operations) + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +void OPPROTO glue(op_movl_A0,REGNAME)(void) +{ + A0 = (uint32_t)REG; +} + +void OPPROTO glue(op_addl_A0,REGNAME)(void) +{ + A0 = (uint32_t)(A0 + REG); +} + +void OPPROTO glue(glue(op_addl_A0,REGNAME),_s1)(void) +{ + A0 = (uint32_t)(A0 + (REG << 1)); +} + +void OPPROTO glue(glue(op_addl_A0,REGNAME),_s2)(void) +{ + A0 = (uint32_t)(A0 + (REG << 2)); +} + +void OPPROTO glue(glue(op_addl_A0,REGNAME),_s3)(void) +{ + A0 = (uint32_t)(A0 + (REG << 3)); +} + +#ifdef TARGET_X86_64 +void OPPROTO glue(op_movq_A0,REGNAME)(void) +{ + A0 = REG; +} + +void OPPROTO glue(op_addq_A0,REGNAME)(void) +{ + A0 = (A0 + REG); +} + +void OPPROTO glue(glue(op_addq_A0,REGNAME),_s1)(void) +{ + A0 = (A0 + (REG << 1)); +} + +void OPPROTO glue(glue(op_addq_A0,REGNAME),_s2)(void) +{ + A0 = (A0 + (REG << 2)); +} + +void OPPROTO glue(glue(op_addq_A0,REGNAME),_s3)(void) +{ + A0 = (A0 + (REG << 3)); +} +#endif + +void OPPROTO glue(op_movl_T0,REGNAME)(void) +{ + T0 = REG; +} + +void OPPROTO glue(op_movl_T1,REGNAME)(void) +{ + T1 = REG; +} + +void OPPROTO glue(op_movh_T0,REGNAME)(void) +{ + T0 = REG >> 8; +} + +void OPPROTO glue(op_movh_T1,REGNAME)(void) +{ + T1 = REG >> 8; +} + +void OPPROTO glue(glue(op_movl,REGNAME),_T0)(void) +{ + REG = (uint32_t)T0; +} + +void OPPROTO glue(glue(op_movl,REGNAME),_T1)(void) +{ + REG = (uint32_t)T1; +} + +void OPPROTO glue(glue(op_movl,REGNAME),_A0)(void) +{ + REG = (uint32_t)A0; +} + +#ifdef TARGET_X86_64 +void OPPROTO glue(glue(op_movq,REGNAME),_T0)(void) +{ + REG = T0; +} + +void OPPROTO glue(glue(op_movq,REGNAME),_T1)(void) +{ + REG = T1; +} + +void OPPROTO glue(glue(op_movq,REGNAME),_A0)(void) +{ + REG = A0; +} +#endif + +/* mov T1 to REG if T0 is true */ +void OPPROTO glue(glue(op_cmovw,REGNAME),_T1_T0)(void) +{ + if (T0) + REG = (REG & ~0xffff) | (T1 & 0xffff); + FORCE_RET(); +} + +void OPPROTO glue(glue(op_cmovl,REGNAME),_T1_T0)(void) +{ + if (T0) + REG = (uint32_t)T1; + FORCE_RET(); +} + +#ifdef TARGET_X86_64 +void OPPROTO glue(glue(op_cmovq,REGNAME),_T1_T0)(void) +{ + if (T0) + REG = T1; + FORCE_RET(); +} +#endif + +/* NOTE: T0 high order bits are ignored */ +void OPPROTO glue(glue(op_movw,REGNAME),_T0)(void) +{ + REG = (REG & ~0xffff) | (T0 & 0xffff); +} + +/* NOTE: T0 high order bits are ignored */ +void OPPROTO glue(glue(op_movw,REGNAME),_T1)(void) +{ + REG = (REG & ~0xffff) | (T1 & 0xffff); +} + +/* NOTE: A0 high order bits are ignored */ +void OPPROTO glue(glue(op_movw,REGNAME),_A0)(void) +{ + REG = (REG & ~0xffff) | (A0 & 0xffff); +} + +/* NOTE: T0 high order bits are ignored */ +void OPPROTO glue(glue(op_movb,REGNAME),_T0)(void) +{ + REG = (REG & ~0xff) | (T0 & 0xff); +} + +/* NOTE: T0 high order bits are ignored */ +void OPPROTO glue(glue(op_movh,REGNAME),_T0)(void) +{ + REG = (REG & ~0xff00) | ((T0 & 0xff) << 8); +} + +/* NOTE: T1 high order bits are ignored */ +void OPPROTO glue(glue(op_movb,REGNAME),_T1)(void) +{ + REG = (REG & ~0xff) | (T1 & 0xff); +} + +/* NOTE: T1 high order bits are ignored */ +void OPPROTO glue(glue(op_movh,REGNAME),_T1)(void) +{ + REG = (REG & ~0xff00) | ((T1 & 0xff) << 8); +} + diff --git a/tools/ioemu/target-i386/ops_mem.h b/tools/ioemu/target-i386/ops_mem.h new file mode 100644 index 0000000000..7ec84dde89 --- /dev/null +++ b/tools/ioemu/target-i386/ops_mem.h @@ -0,0 +1,156 @@ +void OPPROTO glue(glue(op_ldub, MEMSUFFIX), _T0_A0)(void) +{ + T0 = glue(ldub, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_ldsb, MEMSUFFIX), _T0_A0)(void) +{ + T0 = glue(ldsb, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_lduw, MEMSUFFIX), _T0_A0)(void) +{ + T0 = glue(lduw, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_ldsw, MEMSUFFIX), _T0_A0)(void) +{ + T0 = glue(ldsw, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_ldl, MEMSUFFIX), _T0_A0)(void) +{ + T0 = (uint32_t)glue(ldl, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_ldub, MEMSUFFIX), _T1_A0)(void) +{ + T1 = glue(ldub, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_ldsb, MEMSUFFIX), _T1_A0)(void) +{ + T1 = glue(ldsb, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_lduw, MEMSUFFIX), _T1_A0)(void) +{ + T1 = glue(lduw, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_ldsw, MEMSUFFIX), _T1_A0)(void) +{ + T1 = glue(ldsw, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_ldl, MEMSUFFIX), _T1_A0)(void) +{ + T1 = (uint32_t)glue(ldl, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_stb, MEMSUFFIX), _T0_A0)(void) +{ + glue(stb, MEMSUFFIX)(A0, T0); + FORCE_RET(); +} + +void OPPROTO glue(glue(op_stw, MEMSUFFIX), _T0_A0)(void) +{ + glue(stw, MEMSUFFIX)(A0, T0); + FORCE_RET(); +} + +void OPPROTO glue(glue(op_stl, MEMSUFFIX), _T0_A0)(void) +{ + glue(stl, MEMSUFFIX)(A0, T0); + FORCE_RET(); +} + +#if 0 +void OPPROTO glue(glue(op_stb, MEMSUFFIX), _T1_A0)(void) +{ + glue(stb, MEMSUFFIX)(A0, T1); + FORCE_RET(); +} +#endif + +void OPPROTO glue(glue(op_stw, MEMSUFFIX), _T1_A0)(void) +{ + glue(stw, MEMSUFFIX)(A0, T1); + FORCE_RET(); +} + +void OPPROTO glue(glue(op_stl, MEMSUFFIX), _T1_A0)(void) +{ + glue(stl, MEMSUFFIX)(A0, T1); + FORCE_RET(); +} + +/* SSE/MMX support */ +void OPPROTO glue(glue(op_ldq, MEMSUFFIX), _env_A0)(void) +{ + uint64_t *p; + p = (uint64_t *)((char *)env + PARAM1); + *p = glue(ldq, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_stq, MEMSUFFIX), _env_A0)(void) +{ + uint64_t *p; + p = (uint64_t *)((char *)env + PARAM1); + glue(stq, MEMSUFFIX)(A0, *p); + FORCE_RET(); +} + +void OPPROTO glue(glue(op_ldo, MEMSUFFIX), _env_A0)(void) +{ + XMMReg *p; + p = (XMMReg *)((char *)env + PARAM1); + p->XMM_Q(0) = glue(ldq, MEMSUFFIX)(A0); + p->XMM_Q(1) = glue(ldq, MEMSUFFIX)(A0 + 8); +} + +void OPPROTO glue(glue(op_sto, MEMSUFFIX), _env_A0)(void) +{ + XMMReg *p; + p = (XMMReg *)((char *)env + PARAM1); + glue(stq, MEMSUFFIX)(A0, p->XMM_Q(0)); + glue(stq, MEMSUFFIX)(A0 + 8, p->XMM_Q(1)); + FORCE_RET(); +} + +#ifdef TARGET_X86_64 +void OPPROTO glue(glue(op_ldsl, MEMSUFFIX), _T0_A0)(void) +{ + T0 = (int32_t)glue(ldl, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_ldsl, MEMSUFFIX), _T1_A0)(void) +{ + T1 = (int32_t)glue(ldl, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_ldq, MEMSUFFIX), _T0_A0)(void) +{ + T0 = glue(ldq, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_ldq, MEMSUFFIX), _T1_A0)(void) +{ + T1 = glue(ldq, MEMSUFFIX)(A0); +} + +void OPPROTO glue(glue(op_stq, MEMSUFFIX), _T0_A0)(void) +{ + glue(stq, MEMSUFFIX)(A0, T0); + FORCE_RET(); +} + +void OPPROTO glue(glue(op_stq, MEMSUFFIX), _T1_A0)(void) +{ + glue(stq, MEMSUFFIX)(A0, T1); + FORCE_RET(); +} +#endif + +#undef MEMSUFFIX diff --git a/tools/ioemu/target-i386/ops_sse.h b/tools/ioemu/target-i386/ops_sse.h new file mode 100644 index 0000000000..cdc3801200 --- /dev/null +++ b/tools/ioemu/target-i386/ops_sse.h @@ -0,0 +1,1374 @@ +/* + * MMX/SSE/SSE2/PNI support + * + * Copyright (c) 2005 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#if SHIFT == 0 +#define Reg MMXReg +#define XMM_ONLY(x...) +#define B(n) MMX_B(n) +#define W(n) MMX_W(n) +#define L(n) MMX_L(n) +#define Q(n) q +#define SUFFIX _mmx +#else +#define Reg XMMReg +#define XMM_ONLY(x...) x +#define B(n) XMM_B(n) +#define W(n) XMM_W(n) +#define L(n) XMM_L(n) +#define Q(n) XMM_Q(n) +#define SUFFIX _xmm +#endif + +void OPPROTO glue(op_psrlw, SUFFIX)(void) +{ + Reg *d, *s; + int shift; + + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + if (s->Q(0) > 15) { + d->Q(0) = 0; +#if SHIFT == 1 + d->Q(1) = 0; +#endif + } else { + shift = s->B(0); + d->W(0) >>= shift; + d->W(1) >>= shift; + d->W(2) >>= shift; + d->W(3) >>= shift; +#if SHIFT == 1 + d->W(4) >>= shift; + d->W(5) >>= shift; + d->W(6) >>= shift; + d->W(7) >>= shift; +#endif + } + FORCE_RET(); +} + +void OPPROTO glue(op_psraw, SUFFIX)(void) +{ + Reg *d, *s; + int shift; + + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + if (s->Q(0) > 15) { + shift = 15; + } else { + shift = s->B(0); + } + d->W(0) = (int16_t)d->W(0) >> shift; + d->W(1) = (int16_t)d->W(1) >> shift; + d->W(2) = (int16_t)d->W(2) >> shift; + d->W(3) = (int16_t)d->W(3) >> shift; +#if SHIFT == 1 + d->W(4) = (int16_t)d->W(4) >> shift; + d->W(5) = (int16_t)d->W(5) >> shift; + d->W(6) = (int16_t)d->W(6) >> shift; + d->W(7) = (int16_t)d->W(7) >> shift; +#endif +} + +void OPPROTO glue(op_psllw, SUFFIX)(void) +{ + Reg *d, *s; + int shift; + + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + if (s->Q(0) > 15) { + d->Q(0) = 0; +#if SHIFT == 1 + d->Q(1) = 0; +#endif + } else { + shift = s->B(0); + d->W(0) <<= shift; + d->W(1) <<= shift; + d->W(2) <<= shift; + d->W(3) <<= shift; +#if SHIFT == 1 + d->W(4) <<= shift; + d->W(5) <<= shift; + d->W(6) <<= shift; + d->W(7) <<= shift; +#endif + } + FORCE_RET(); +} + +void OPPROTO glue(op_psrld, SUFFIX)(void) +{ + Reg *d, *s; + int shift; + + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + if (s->Q(0) > 31) { + d->Q(0) = 0; +#if SHIFT == 1 + d->Q(1) = 0; +#endif + } else { + shift = s->B(0); + d->L(0) >>= shift; + d->L(1) >>= shift; +#if SHIFT == 1 + d->L(2) >>= shift; + d->L(3) >>= shift; +#endif + } + FORCE_RET(); +} + +void OPPROTO glue(op_psrad, SUFFIX)(void) +{ + Reg *d, *s; + int shift; + + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + if (s->Q(0) > 31) { + shift = 31; + } else { + shift = s->B(0); + } + d->L(0) = (int32_t)d->L(0) >> shift; + d->L(1) = (int32_t)d->L(1) >> shift; +#if SHIFT == 1 + d->L(2) = (int32_t)d->L(2) >> shift; + d->L(3) = (int32_t)d->L(3) >> shift; +#endif +} + +void OPPROTO glue(op_pslld, SUFFIX)(void) +{ + Reg *d, *s; + int shift; + + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + if (s->Q(0) > 31) { + d->Q(0) = 0; +#if SHIFT == 1 + d->Q(1) = 0; +#endif + } else { + shift = s->B(0); + d->L(0) <<= shift; + d->L(1) <<= shift; +#if SHIFT == 1 + d->L(2) <<= shift; + d->L(3) <<= shift; +#endif + } + FORCE_RET(); +} + +void OPPROTO glue(op_psrlq, SUFFIX)(void) +{ + Reg *d, *s; + int shift; + + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + if (s->Q(0) > 63) { + d->Q(0) = 0; +#if SHIFT == 1 + d->Q(1) = 0; +#endif + } else { + shift = s->B(0); + d->Q(0) >>= shift; +#if SHIFT == 1 + d->Q(1) >>= shift; +#endif + } + FORCE_RET(); +} + +void OPPROTO glue(op_psllq, SUFFIX)(void) +{ + Reg *d, *s; + int shift; + + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + if (s->Q(0) > 63) { + d->Q(0) = 0; +#if SHIFT == 1 + d->Q(1) = 0; +#endif + } else { + shift = s->B(0); + d->Q(0) <<= shift; +#if SHIFT == 1 + d->Q(1) <<= shift; +#endif + } + FORCE_RET(); +} + +#if SHIFT == 1 +void OPPROTO glue(op_psrldq, SUFFIX)(void) +{ + Reg *d, *s; + int shift, i; + + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + shift = s->L(0); + if (shift > 16) + shift = 16; + for(i = 0; i < 16 - shift; i++) + d->B(i) = d->B(i + shift); + for(i = 16 - shift; i < 16; i++) + d->B(i) = 0; + FORCE_RET(); +} + +void OPPROTO glue(op_pslldq, SUFFIX)(void) +{ + Reg *d, *s; + int shift, i; + + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + shift = s->L(0); + if (shift > 16) + shift = 16; + for(i = 15; i >= shift; i--) + d->B(i) = d->B(i - shift); + for(i = 0; i < shift; i++) + d->B(i) = 0; + FORCE_RET(); +} +#endif + +#define SSE_OP_B(name, F)\ +void OPPROTO glue(name, SUFFIX) (void)\ +{\ + Reg *d, *s;\ + d = (Reg *)((char *)env + PARAM1);\ + s = (Reg *)((char *)env + PARAM2);\ + d->B(0) = F(d->B(0), s->B(0));\ + d->B(1) = F(d->B(1), s->B(1));\ + d->B(2) = F(d->B(2), s->B(2));\ + d->B(3) = F(d->B(3), s->B(3));\ + d->B(4) = F(d->B(4), s->B(4));\ + d->B(5) = F(d->B(5), s->B(5));\ + d->B(6) = F(d->B(6), s->B(6));\ + d->B(7) = F(d->B(7), s->B(7));\ + XMM_ONLY(\ + d->B(8) = F(d->B(8), s->B(8));\ + d->B(9) = F(d->B(9), s->B(9));\ + d->B(10) = F(d->B(10), s->B(10));\ + d->B(11) = F(d->B(11), s->B(11));\ + d->B(12) = F(d->B(12), s->B(12));\ + d->B(13) = F(d->B(13), s->B(13));\ + d->B(14) = F(d->B(14), s->B(14));\ + d->B(15) = F(d->B(15), s->B(15));\ + )\ +} + +#define SSE_OP_W(name, F)\ +void OPPROTO glue(name, SUFFIX) (void)\ +{\ + Reg *d, *s;\ + d = (Reg *)((char *)env + PARAM1);\ + s = (Reg *)((char *)env + PARAM2);\ + d->W(0) = F(d->W(0), s->W(0));\ + d->W(1) = F(d->W(1), s->W(1));\ + d->W(2) = F(d->W(2), s->W(2));\ + d->W(3) = F(d->W(3), s->W(3));\ + XMM_ONLY(\ + d->W(4) = F(d->W(4), s->W(4));\ + d->W(5) = F(d->W(5), s->W(5));\ + d->W(6) = F(d->W(6), s->W(6));\ + d->W(7) = F(d->W(7), s->W(7));\ + )\ +} + +#define SSE_OP_L(name, F)\ +void OPPROTO glue(name, SUFFIX) (void)\ +{\ + Reg *d, *s;\ + d = (Reg *)((char *)env + PARAM1);\ + s = (Reg *)((char *)env + PARAM2);\ + d->L(0) = F(d->L(0), s->L(0));\ + d->L(1) = F(d->L(1), s->L(1));\ + XMM_ONLY(\ + d->L(2) = F(d->L(2), s->L(2));\ + d->L(3) = F(d->L(3), s->L(3));\ + )\ +} + +#define SSE_OP_Q(name, F)\ +void OPPROTO glue(name, SUFFIX) (void)\ +{\ + Reg *d, *s;\ + d = (Reg *)((char *)env + PARAM1);\ + s = (Reg *)((char *)env + PARAM2);\ + d->Q(0) = F(d->Q(0), s->Q(0));\ + XMM_ONLY(\ + d->Q(1) = F(d->Q(1), s->Q(1));\ + )\ +} + +#if SHIFT == 0 +static inline int satub(int x) +{ + if (x < 0) + return 0; + else if (x > 255) + return 255; + else + return x; +} + +static inline int satuw(int x) +{ + if (x < 0) + return 0; + else if (x > 65535) + return 65535; + else + return x; +} + +static inline int satsb(int x) +{ + if (x < -128) + return -128; + else if (x > 127) + return 127; + else + return x; +} + +static inline int satsw(int x) +{ + if (x < -32768) + return -32768; + else if (x > 32767) + return 32767; + else + return x; +} + +#define FADD(a, b) ((a) + (b)) +#define FADDUB(a, b) satub((a) + (b)) +#define FADDUW(a, b) satuw((a) + (b)) +#define FADDSB(a, b) satsb((int8_t)(a) + (int8_t)(b)) +#define FADDSW(a, b) satsw((int16_t)(a) + (int16_t)(b)) + +#define FSUB(a, b) ((a) - (b)) +#define FSUBUB(a, b) satub((a) - (b)) +#define FSUBUW(a, b) satuw((a) - (b)) +#define FSUBSB(a, b) satsb((int8_t)(a) - (int8_t)(b)) +#define FSUBSW(a, b) satsw((int16_t)(a) - (int16_t)(b)) +#define FMINUB(a, b) ((a) < (b)) ? (a) : (b) +#define FMINSW(a, b) ((int16_t)(a) < (int16_t)(b)) ? (a) : (b) +#define FMAXUB(a, b) ((a) > (b)) ? (a) : (b) +#define FMAXSW(a, b) ((int16_t)(a) > (int16_t)(b)) ? (a) : (b) + +#define FAND(a, b) (a) & (b) +#define FANDN(a, b) ((~(a)) & (b)) +#define FOR(a, b) (a) | (b) +#define FXOR(a, b) (a) ^ (b) + +#define FCMPGTB(a, b) (int8_t)(a) > (int8_t)(b) ? -1 : 0 +#define FCMPGTW(a, b) (int16_t)(a) > (int16_t)(b) ? -1 : 0 +#define FCMPGTL(a, b) (int32_t)(a) > (int32_t)(b) ? -1 : 0 +#define FCMPEQ(a, b) (a) == (b) ? -1 : 0 + +#define FMULLW(a, b) (a) * (b) +#define FMULHUW(a, b) (a) * (b) >> 16 +#define FMULHW(a, b) (int16_t)(a) * (int16_t)(b) >> 16 + +#define FAVG(a, b) ((a) + (b) + 1) >> 1 +#endif + +SSE_OP_B(op_paddb, FADD) +SSE_OP_W(op_paddw, FADD) +SSE_OP_L(op_paddl, FADD) +SSE_OP_Q(op_paddq, FADD) + +SSE_OP_B(op_psubb, FSUB) +SSE_OP_W(op_psubw, FSUB) +SSE_OP_L(op_psubl, FSUB) +SSE_OP_Q(op_psubq, FSUB) + +SSE_OP_B(op_paddusb, FADDUB) +SSE_OP_B(op_paddsb, FADDSB) +SSE_OP_B(op_psubusb, FSUBUB) +SSE_OP_B(op_psubsb, FSUBSB) + +SSE_OP_W(op_paddusw, FADDUW) +SSE_OP_W(op_paddsw, FADDSW) +SSE_OP_W(op_psubusw, FSUBUW) +SSE_OP_W(op_psubsw, FSUBSW) + +SSE_OP_B(op_pminub, FMINUB) +SSE_OP_B(op_pmaxub, FMAXUB) + +SSE_OP_W(op_pminsw, FMINSW) +SSE_OP_W(op_pmaxsw, FMAXSW) + +SSE_OP_Q(op_pand, FAND) +SSE_OP_Q(op_pandn, FANDN) +SSE_OP_Q(op_por, FOR) +SSE_OP_Q(op_pxor, FXOR) + +SSE_OP_B(op_pcmpgtb, FCMPGTB) +SSE_OP_W(op_pcmpgtw, FCMPGTW) +SSE_OP_L(op_pcmpgtl, FCMPGTL) + +SSE_OP_B(op_pcmpeqb, FCMPEQ) +SSE_OP_W(op_pcmpeqw, FCMPEQ) +SSE_OP_L(op_pcmpeql, FCMPEQ) + +SSE_OP_W(op_pmullw, FMULLW) +SSE_OP_W(op_pmulhuw, FMULHUW) +SSE_OP_W(op_pmulhw, FMULHW) + +SSE_OP_B(op_pavgb, FAVG) +SSE_OP_W(op_pavgw, FAVG) + +void OPPROTO glue(op_pmuludq, SUFFIX) (void) +{ + Reg *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + d->Q(0) = (uint64_t)s->L(0) * (uint64_t)d->L(0); +#if SHIFT == 1 + d->Q(1) = (uint64_t)s->L(2) * (uint64_t)d->L(2); +#endif +} + +void OPPROTO glue(op_pmaddwd, SUFFIX) (void) +{ + int i; + Reg *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + for(i = 0; i < (2 << SHIFT); i++) { + d->L(i) = (int16_t)s->W(2*i) * (int16_t)d->W(2*i) + + (int16_t)s->W(2*i+1) * (int16_t)d->W(2*i+1); + } + FORCE_RET(); +} + +#if SHIFT == 0 +static inline int abs1(int a) +{ + if (a < 0) + return -a; + else + return a; +} +#endif +void OPPROTO glue(op_psadbw, SUFFIX) (void) +{ + unsigned int val; + Reg *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + val = 0; + val += abs1(d->B(0) - s->B(0)); + val += abs1(d->B(1) - s->B(1)); + val += abs1(d->B(2) - s->B(2)); + val += abs1(d->B(3) - s->B(3)); + val += abs1(d->B(4) - s->B(4)); + val += abs1(d->B(5) - s->B(5)); + val += abs1(d->B(6) - s->B(6)); + val += abs1(d->B(7) - s->B(7)); + d->Q(0) = val; +#if SHIFT == 1 + val = 0; + val += abs1(d->B(8) - s->B(8)); + val += abs1(d->B(9) - s->B(9)); + val += abs1(d->B(10) - s->B(10)); + val += abs1(d->B(11) - s->B(11)); + val += abs1(d->B(12) - s->B(12)); + val += abs1(d->B(13) - s->B(13)); + val += abs1(d->B(14) - s->B(14)); + val += abs1(d->B(15) - s->B(15)); + d->Q(1) = val; +#endif +} + +void OPPROTO glue(op_maskmov, SUFFIX) (void) +{ + int i; + Reg *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + for(i = 0; i < (8 << SHIFT); i++) { + if (s->B(i) & 0x80) + stb(A0 + i, d->B(i)); + } + FORCE_RET(); +} + +void OPPROTO glue(op_movl_mm_T0, SUFFIX) (void) +{ + Reg *d; + d = (Reg *)((char *)env + PARAM1); + d->L(0) = T0; + d->L(1) = 0; +#if SHIFT == 1 + d->Q(1) = 0; +#endif +} + +void OPPROTO glue(op_movl_T0_mm, SUFFIX) (void) +{ + Reg *s; + s = (Reg *)((char *)env + PARAM1); + T0 = s->L(0); +} + +#if SHIFT == 0 +void OPPROTO glue(op_pshufw, SUFFIX) (void) +{ + Reg r, *d, *s; + int order; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + order = PARAM3; + r.W(0) = s->W(order & 3); + r.W(1) = s->W((order >> 2) & 3); + r.W(2) = s->W((order >> 4) & 3); + r.W(3) = s->W((order >> 6) & 3); + *d = r; +} +#else +void OPPROTO op_shufps(void) +{ + Reg r, *d, *s; + int order; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + order = PARAM3; + r.L(0) = d->L(order & 3); + r.L(1) = d->L((order >> 2) & 3); + r.L(2) = s->L((order >> 4) & 3); + r.L(3) = s->L((order >> 6) & 3); + *d = r; +} + +void OPPROTO op_shufpd(void) +{ + Reg r, *d, *s; + int order; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + order = PARAM3; + r.Q(0) = d->Q(order & 1); + r.Q(1) = s->Q((order >> 1) & 1); + *d = r; +} + +void OPPROTO glue(op_pshufd, SUFFIX) (void) +{ + Reg r, *d, *s; + int order; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + order = PARAM3; + r.L(0) = s->L(order & 3); + r.L(1) = s->L((order >> 2) & 3); + r.L(2) = s->L((order >> 4) & 3); + r.L(3) = s->L((order >> 6) & 3); + *d = r; +} + +void OPPROTO glue(op_pshuflw, SUFFIX) (void) +{ + Reg r, *d, *s; + int order; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + order = PARAM3; + r.W(0) = s->W(order & 3); + r.W(1) = s->W((order >> 2) & 3); + r.W(2) = s->W((order >> 4) & 3); + r.W(3) = s->W((order >> 6) & 3); + r.Q(1) = s->Q(1); + *d = r; +} + +void OPPROTO glue(op_pshufhw, SUFFIX) (void) +{ + Reg r, *d, *s; + int order; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + order = PARAM3; + r.Q(0) = s->Q(0); + r.W(4) = s->W(4 + (order & 3)); + r.W(5) = s->W(4 + ((order >> 2) & 3)); + r.W(6) = s->W(4 + ((order >> 4) & 3)); + r.W(7) = s->W(4 + ((order >> 6) & 3)); + *d = r; +} +#endif + +#if SHIFT == 1 +/* FPU ops */ +/* XXX: not accurate */ + +#define SSE_OP_S(name, F)\ +void OPPROTO op_ ## name ## ps (void)\ +{\ + Reg *d, *s;\ + d = (Reg *)((char *)env + PARAM1);\ + s = (Reg *)((char *)env + PARAM2);\ + d->XMM_S(0) = F(32, d->XMM_S(0), s->XMM_S(0));\ + d->XMM_S(1) = F(32, d->XMM_S(1), s->XMM_S(1));\ + d->XMM_S(2) = F(32, d->XMM_S(2), s->XMM_S(2));\ + d->XMM_S(3) = F(32, d->XMM_S(3), s->XMM_S(3));\ +}\ +\ +void OPPROTO op_ ## name ## ss (void)\ +{\ + Reg *d, *s;\ + d = (Reg *)((char *)env + PARAM1);\ + s = (Reg *)((char *)env + PARAM2);\ + d->XMM_S(0) = F(32, d->XMM_S(0), s->XMM_S(0));\ +}\ +void OPPROTO op_ ## name ## pd (void)\ +{\ + Reg *d, *s;\ + d = (Reg *)((char *)env + PARAM1);\ + s = (Reg *)((char *)env + PARAM2);\ + d->XMM_D(0) = F(64, d->XMM_D(0), s->XMM_D(0));\ + d->XMM_D(1) = F(64, d->XMM_D(1), s->XMM_D(1));\ +}\ +\ +void OPPROTO op_ ## name ## sd (void)\ +{\ + Reg *d, *s;\ + d = (Reg *)((char *)env + PARAM1);\ + s = (Reg *)((char *)env + PARAM2);\ + d->XMM_D(0) = F(64, d->XMM_D(0), s->XMM_D(0));\ +} + +#define FPU_ADD(size, a, b) float ## size ## _add(a, b, &env->sse_status) +#define FPU_SUB(size, a, b) float ## size ## _sub(a, b, &env->sse_status) +#define FPU_MUL(size, a, b) float ## size ## _mul(a, b, &env->sse_status) +#define FPU_DIV(size, a, b) float ## size ## _div(a, b, &env->sse_status) +#define FPU_MIN(size, a, b) (a) < (b) ? (a) : (b) +#define FPU_MAX(size, a, b) (a) > (b) ? (a) : (b) +#define FPU_SQRT(size, a, b) float ## size ## _sqrt(b, &env->sse_status) + +SSE_OP_S(add, FPU_ADD) +SSE_OP_S(sub, FPU_SUB) +SSE_OP_S(mul, FPU_MUL) +SSE_OP_S(div, FPU_DIV) +SSE_OP_S(min, FPU_MIN) +SSE_OP_S(max, FPU_MAX) +SSE_OP_S(sqrt, FPU_SQRT) + + +/* float to float conversions */ +void OPPROTO op_cvtps2pd(void) +{ + float32 s0, s1; + Reg *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + s0 = s->XMM_S(0); + s1 = s->XMM_S(1); + d->XMM_D(0) = float32_to_float64(s0, &env->sse_status); + d->XMM_D(1) = float32_to_float64(s1, &env->sse_status); +} + +void OPPROTO op_cvtpd2ps(void) +{ + Reg *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + d->XMM_S(0) = float64_to_float32(s->XMM_D(0), &env->sse_status); + d->XMM_S(1) = float64_to_float32(s->XMM_D(1), &env->sse_status); + d->Q(1) = 0; +} + +void OPPROTO op_cvtss2sd(void) +{ + Reg *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + d->XMM_D(0) = float32_to_float64(s->XMM_S(0), &env->sse_status); +} + +void OPPROTO op_cvtsd2ss(void) +{ + Reg *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + d->XMM_S(0) = float64_to_float32(s->XMM_D(0), &env->sse_status); +} + +/* integer to float */ +void OPPROTO op_cvtdq2ps(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->XMM_S(0) = int32_to_float32(s->XMM_L(0), &env->sse_status); + d->XMM_S(1) = int32_to_float32(s->XMM_L(1), &env->sse_status); + d->XMM_S(2) = int32_to_float32(s->XMM_L(2), &env->sse_status); + d->XMM_S(3) = int32_to_float32(s->XMM_L(3), &env->sse_status); +} + +void OPPROTO op_cvtdq2pd(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + int32_t l0, l1; + l0 = (int32_t)s->XMM_L(0); + l1 = (int32_t)s->XMM_L(1); + d->XMM_D(0) = int32_to_float64(l0, &env->sse_status); + d->XMM_D(1) = int32_to_float64(l1, &env->sse_status); +} + +void OPPROTO op_cvtpi2ps(void) +{ + XMMReg *d = (Reg *)((char *)env + PARAM1); + MMXReg *s = (MMXReg *)((char *)env + PARAM2); + d->XMM_S(0) = int32_to_float32(s->MMX_L(0), &env->sse_status); + d->XMM_S(1) = int32_to_float32(s->MMX_L(1), &env->sse_status); +} + +void OPPROTO op_cvtpi2pd(void) +{ + XMMReg *d = (Reg *)((char *)env + PARAM1); + MMXReg *s = (MMXReg *)((char *)env + PARAM2); + d->XMM_D(0) = int32_to_float64(s->MMX_L(0), &env->sse_status); + d->XMM_D(1) = int32_to_float64(s->MMX_L(1), &env->sse_status); +} + +void OPPROTO op_cvtsi2ss(void) +{ + XMMReg *d = (Reg *)((char *)env + PARAM1); + d->XMM_S(0) = int32_to_float32(T0, &env->sse_status); +} + +void OPPROTO op_cvtsi2sd(void) +{ + XMMReg *d = (Reg *)((char *)env + PARAM1); + d->XMM_D(0) = int32_to_float64(T0, &env->sse_status); +} + +#ifdef TARGET_X86_64 +void OPPROTO op_cvtsq2ss(void) +{ + XMMReg *d = (Reg *)((char *)env + PARAM1); + d->XMM_S(0) = int64_to_float32(T0, &env->sse_status); +} + +void OPPROTO op_cvtsq2sd(void) +{ + XMMReg *d = (Reg *)((char *)env + PARAM1); + d->XMM_D(0) = int64_to_float64(T0, &env->sse_status); +} +#endif + +/* float to integer */ +void OPPROTO op_cvtps2dq(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->XMM_L(0) = float32_to_int32(s->XMM_S(0), &env->sse_status); + d->XMM_L(1) = float32_to_int32(s->XMM_S(1), &env->sse_status); + d->XMM_L(2) = float32_to_int32(s->XMM_S(2), &env->sse_status); + d->XMM_L(3) = float32_to_int32(s->XMM_S(3), &env->sse_status); +} + +void OPPROTO op_cvtpd2dq(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->XMM_L(0) = float64_to_int32(s->XMM_D(0), &env->sse_status); + d->XMM_L(1) = float64_to_int32(s->XMM_D(1), &env->sse_status); + d->XMM_Q(1) = 0; +} + +void OPPROTO op_cvtps2pi(void) +{ + MMXReg *d = (MMXReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->MMX_L(0) = float32_to_int32(s->XMM_S(0), &env->sse_status); + d->MMX_L(1) = float32_to_int32(s->XMM_S(1), &env->sse_status); +} + +void OPPROTO op_cvtpd2pi(void) +{ + MMXReg *d = (MMXReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->MMX_L(0) = float64_to_int32(s->XMM_D(0), &env->sse_status); + d->MMX_L(1) = float64_to_int32(s->XMM_D(1), &env->sse_status); +} + +void OPPROTO op_cvtss2si(void) +{ + XMMReg *s = (XMMReg *)((char *)env + PARAM1); + T0 = float32_to_int32(s->XMM_S(0), &env->sse_status); +} + +void OPPROTO op_cvtsd2si(void) +{ + XMMReg *s = (XMMReg *)((char *)env + PARAM1); + T0 = float64_to_int32(s->XMM_D(0), &env->sse_status); +} + +#ifdef TARGET_X86_64 +void OPPROTO op_cvtss2sq(void) +{ + XMMReg *s = (XMMReg *)((char *)env + PARAM1); + T0 = float32_to_int64(s->XMM_S(0), &env->sse_status); +} + +void OPPROTO op_cvtsd2sq(void) +{ + XMMReg *s = (XMMReg *)((char *)env + PARAM1); + T0 = float64_to_int64(s->XMM_D(0), &env->sse_status); +} +#endif + +/* float to integer truncated */ +void OPPROTO op_cvttps2dq(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->XMM_L(0) = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status); + d->XMM_L(1) = float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status); + d->XMM_L(2) = float32_to_int32_round_to_zero(s->XMM_S(2), &env->sse_status); + d->XMM_L(3) = float32_to_int32_round_to_zero(s->XMM_S(3), &env->sse_status); +} + +void OPPROTO op_cvttpd2dq(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->XMM_L(0) = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status); + d->XMM_L(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status); + d->XMM_Q(1) = 0; +} + +void OPPROTO op_cvttps2pi(void) +{ + MMXReg *d = (MMXReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->MMX_L(0) = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status); + d->MMX_L(1) = float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status); +} + +void OPPROTO op_cvttpd2pi(void) +{ + MMXReg *d = (MMXReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->MMX_L(0) = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status); + d->MMX_L(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status); +} + +void OPPROTO op_cvttss2si(void) +{ + XMMReg *s = (XMMReg *)((char *)env + PARAM1); + T0 = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status); +} + +void OPPROTO op_cvttsd2si(void) +{ + XMMReg *s = (XMMReg *)((char *)env + PARAM1); + T0 = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status); +} + +#ifdef TARGET_X86_64 +void OPPROTO op_cvttss2sq(void) +{ + XMMReg *s = (XMMReg *)((char *)env + PARAM1); + T0 = float32_to_int64_round_to_zero(s->XMM_S(0), &env->sse_status); +} + +void OPPROTO op_cvttsd2sq(void) +{ + XMMReg *s = (XMMReg *)((char *)env + PARAM1); + T0 = float64_to_int64_round_to_zero(s->XMM_D(0), &env->sse_status); +} +#endif + +void OPPROTO op_rsqrtps(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->XMM_S(0) = approx_rsqrt(s->XMM_S(0)); + d->XMM_S(1) = approx_rsqrt(s->XMM_S(1)); + d->XMM_S(2) = approx_rsqrt(s->XMM_S(2)); + d->XMM_S(3) = approx_rsqrt(s->XMM_S(3)); +} + +void OPPROTO op_rsqrtss(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->XMM_S(0) = approx_rsqrt(s->XMM_S(0)); +} + +void OPPROTO op_rcpps(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->XMM_S(0) = approx_rcp(s->XMM_S(0)); + d->XMM_S(1) = approx_rcp(s->XMM_S(1)); + d->XMM_S(2) = approx_rcp(s->XMM_S(2)); + d->XMM_S(3) = approx_rcp(s->XMM_S(3)); +} + +void OPPROTO op_rcpss(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->XMM_S(0) = approx_rcp(s->XMM_S(0)); +} + +void OPPROTO op_haddps(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + XMMReg r; + r.XMM_S(0) = d->XMM_S(0) + d->XMM_S(1); + r.XMM_S(1) = d->XMM_S(2) + d->XMM_S(3); + r.XMM_S(2) = s->XMM_S(0) + s->XMM_S(1); + r.XMM_S(3) = s->XMM_S(2) + s->XMM_S(3); + *d = r; +} + +void OPPROTO op_haddpd(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + XMMReg r; + r.XMM_D(0) = d->XMM_D(0) + d->XMM_D(1); + r.XMM_D(1) = s->XMM_D(0) + s->XMM_D(1); + *d = r; +} + +void OPPROTO op_hsubps(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + XMMReg r; + r.XMM_S(0) = d->XMM_S(0) - d->XMM_S(1); + r.XMM_S(1) = d->XMM_S(2) - d->XMM_S(3); + r.XMM_S(2) = s->XMM_S(0) - s->XMM_S(1); + r.XMM_S(3) = s->XMM_S(2) - s->XMM_S(3); + *d = r; +} + +void OPPROTO op_hsubpd(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + XMMReg r; + r.XMM_D(0) = d->XMM_D(0) - d->XMM_D(1); + r.XMM_D(1) = s->XMM_D(0) - s->XMM_D(1); + *d = r; +} + +void OPPROTO op_addsubps(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->XMM_S(0) = d->XMM_S(0) - s->XMM_S(0); + d->XMM_S(1) = d->XMM_S(1) + s->XMM_S(1); + d->XMM_S(2) = d->XMM_S(2) - s->XMM_S(2); + d->XMM_S(3) = d->XMM_S(3) + s->XMM_S(3); +} + +void OPPROTO op_addsubpd(void) +{ + XMMReg *d = (XMMReg *)((char *)env + PARAM1); + XMMReg *s = (XMMReg *)((char *)env + PARAM2); + d->XMM_D(0) = d->XMM_D(0) - s->XMM_D(0); + d->XMM_D(1) = d->XMM_D(1) + s->XMM_D(1); +} + +/* XXX: unordered */ +#define SSE_OP_CMP(name, F)\ +void OPPROTO op_ ## name ## ps (void)\ +{\ + Reg *d, *s;\ + d = (Reg *)((char *)env + PARAM1);\ + s = (Reg *)((char *)env + PARAM2);\ + d->XMM_L(0) = F(32, d->XMM_S(0), s->XMM_S(0));\ + d->XMM_L(1) = F(32, d->XMM_S(1), s->XMM_S(1));\ + d->XMM_L(2) = F(32, d->XMM_S(2), s->XMM_S(2));\ + d->XMM_L(3) = F(32, d->XMM_S(3), s->XMM_S(3));\ +}\ +\ +void OPPROTO op_ ## name ## ss (void)\ +{\ + Reg *d, *s;\ + d = (Reg *)((char *)env + PARAM1);\ + s = (Reg *)((char *)env + PARAM2);\ + d->XMM_L(0) = F(32, d->XMM_S(0), s->XMM_S(0));\ +}\ +void OPPROTO op_ ## name ## pd (void)\ +{\ + Reg *d, *s;\ + d = (Reg *)((char *)env + PARAM1);\ + s = (Reg *)((char *)env + PARAM2);\ + d->XMM_Q(0) = F(64, d->XMM_D(0), s->XMM_D(0));\ + d->XMM_Q(1) = F(64, d->XMM_D(1), s->XMM_D(1));\ +}\ +\ +void OPPROTO op_ ## name ## sd (void)\ +{\ + Reg *d, *s;\ + d = (Reg *)((char *)env + PARAM1);\ + s = (Reg *)((char *)env + PARAM2);\ + d->XMM_Q(0) = F(64, d->XMM_D(0), s->XMM_D(0));\ +} + +#define FPU_CMPEQ(size, a, b) float ## size ## _eq(a, b, &env->sse_status) ? -1 : 0 +#define FPU_CMPLT(size, a, b) float ## size ## _lt(a, b, &env->sse_status) ? -1 : 0 +#define FPU_CMPLE(size, a, b) float ## size ## _le(a, b, &env->sse_status) ? -1 : 0 +#define FPU_CMPUNORD(size, a, b) float ## size ## _unordered(a, b, &env->sse_status) ? - 1 : 0 +#define FPU_CMPNEQ(size, a, b) float ## size ## _eq(a, b, &env->sse_status) ? 0 : -1 +#define FPU_CMPNLT(size, a, b) float ## size ## _lt(a, b, &env->sse_status) ? 0 : -1 +#define FPU_CMPNLE(size, a, b) float ## size ## _le(a, b, &env->sse_status) ? 0 : -1 +#define FPU_CMPORD(size, a, b) float ## size ## _unordered(a, b, &env->sse_status) ? 0 : -1 + +SSE_OP_CMP(cmpeq, FPU_CMPEQ) +SSE_OP_CMP(cmplt, FPU_CMPLT) +SSE_OP_CMP(cmple, FPU_CMPLE) +SSE_OP_CMP(cmpunord, FPU_CMPUNORD) +SSE_OP_CMP(cmpneq, FPU_CMPNEQ) +SSE_OP_CMP(cmpnlt, FPU_CMPNLT) +SSE_OP_CMP(cmpnle, FPU_CMPNLE) +SSE_OP_CMP(cmpord, FPU_CMPORD) + +const int comis_eflags[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C}; + +void OPPROTO op_ucomiss(void) +{ + int ret; + float32 s0, s1; + Reg *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + s0 = d->XMM_S(0); + s1 = s->XMM_S(0); + ret = float32_compare_quiet(s0, s1, &env->sse_status); + CC_SRC = comis_eflags[ret + 1]; + FORCE_RET(); +} + +void OPPROTO op_comiss(void) +{ + int ret; + float32 s0, s1; + Reg *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + s0 = d->XMM_S(0); + s1 = s->XMM_S(0); + ret = float32_compare(s0, s1, &env->sse_status); + CC_SRC = comis_eflags[ret + 1]; + FORCE_RET(); +} + +void OPPROTO op_ucomisd(void) +{ + int ret; + float64 d0, d1; + Reg *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + d0 = d->XMM_D(0); + d1 = s->XMM_D(0); + ret = float64_compare_quiet(d0, d1, &env->sse_status); + CC_SRC = comis_eflags[ret + 1]; + FORCE_RET(); +} + +void OPPROTO op_comisd(void) +{ + int ret; + float64 d0, d1; + Reg *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + d0 = d->XMM_D(0); + d1 = s->XMM_D(0); + ret = float64_compare(d0, d1, &env->sse_status); + CC_SRC = comis_eflags[ret + 1]; + FORCE_RET(); +} + +void OPPROTO op_movmskps(void) +{ + int b0, b1, b2, b3; + Reg *s; + s = (Reg *)((char *)env + PARAM1); + b0 = s->XMM_L(0) >> 31; + b1 = s->XMM_L(1) >> 31; + b2 = s->XMM_L(2) >> 31; + b3 = s->XMM_L(3) >> 31; + T0 = b0 | (b1 << 1) | (b2 << 2) | (b3 << 3); +} + +void OPPROTO op_movmskpd(void) +{ + int b0, b1; + Reg *s; + s = (Reg *)((char *)env + PARAM1); + b0 = s->XMM_L(1) >> 31; + b1 = s->XMM_L(3) >> 31; + T0 = b0 | (b1 << 1); +} + +#endif + +void OPPROTO glue(op_pmovmskb, SUFFIX)(void) +{ + Reg *s; + s = (Reg *)((char *)env + PARAM1); + T0 = 0; + T0 |= (s->XMM_B(0) >> 7); + T0 |= (s->XMM_B(1) >> 6) & 0x02; + T0 |= (s->XMM_B(2) >> 5) & 0x04; + T0 |= (s->XMM_B(3) >> 4) & 0x08; + T0 |= (s->XMM_B(4) >> 3) & 0x10; + T0 |= (s->XMM_B(5) >> 2) & 0x20; + T0 |= (s->XMM_B(6) >> 1) & 0x40; + T0 |= (s->XMM_B(7)) & 0x80; +#if SHIFT == 1 + T0 |= (s->XMM_B(8) << 1) & 0x0100; + T0 |= (s->XMM_B(9) << 2) & 0x0200; + T0 |= (s->XMM_B(10) << 3) & 0x0400; + T0 |= (s->XMM_B(11) << 4) & 0x0800; + T0 |= (s->XMM_B(12) << 5) & 0x1000; + T0 |= (s->XMM_B(13) << 6) & 0x2000; + T0 |= (s->XMM_B(14) << 7) & 0x4000; + T0 |= (s->XMM_B(15) << 8) & 0x8000; +#endif +} + +void OPPROTO glue(op_pinsrw, SUFFIX) (void) +{ + Reg *d = (Reg *)((char *)env + PARAM1); + int pos = PARAM2; + + d->W(pos) = T0; +} + +void OPPROTO glue(op_pextrw, SUFFIX) (void) +{ + Reg *s = (Reg *)((char *)env + PARAM1); + int pos = PARAM2; + + T0 = s->W(pos); +} + +void OPPROTO glue(op_packsswb, SUFFIX) (void) +{ + Reg r, *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + r.B(0) = satsb((int16_t)d->W(0)); + r.B(1) = satsb((int16_t)d->W(1)); + r.B(2) = satsb((int16_t)d->W(2)); + r.B(3) = satsb((int16_t)d->W(3)); +#if SHIFT == 1 + r.B(4) = satsb((int16_t)d->W(4)); + r.B(5) = satsb((int16_t)d->W(5)); + r.B(6) = satsb((int16_t)d->W(6)); + r.B(7) = satsb((int16_t)d->W(7)); +#endif + r.B((4 << SHIFT) + 0) = satsb((int16_t)s->W(0)); + r.B((4 << SHIFT) + 1) = satsb((int16_t)s->W(1)); + r.B((4 << SHIFT) + 2) = satsb((int16_t)s->W(2)); + r.B((4 << SHIFT) + 3) = satsb((int16_t)s->W(3)); +#if SHIFT == 1 + r.B(12) = satsb((int16_t)s->W(4)); + r.B(13) = satsb((int16_t)s->W(5)); + r.B(14) = satsb((int16_t)s->W(6)); + r.B(15) = satsb((int16_t)s->W(7)); +#endif + *d = r; +} + +void OPPROTO glue(op_packuswb, SUFFIX) (void) +{ + Reg r, *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + r.B(0) = satub((int16_t)d->W(0)); + r.B(1) = satub((int16_t)d->W(1)); + r.B(2) = satub((int16_t)d->W(2)); + r.B(3) = satub((int16_t)d->W(3)); +#if SHIFT == 1 + r.B(4) = satub((int16_t)d->W(4)); + r.B(5) = satub((int16_t)d->W(5)); + r.B(6) = satub((int16_t)d->W(6)); + r.B(7) = satub((int16_t)d->W(7)); +#endif + r.B((4 << SHIFT) + 0) = satub((int16_t)s->W(0)); + r.B((4 << SHIFT) + 1) = satub((int16_t)s->W(1)); + r.B((4 << SHIFT) + 2) = satub((int16_t)s->W(2)); + r.B((4 << SHIFT) + 3) = satub((int16_t)s->W(3)); +#if SHIFT == 1 + r.B(12) = satub((int16_t)s->W(4)); + r.B(13) = satub((int16_t)s->W(5)); + r.B(14) = satub((int16_t)s->W(6)); + r.B(15) = satub((int16_t)s->W(7)); +#endif + *d = r; +} + +void OPPROTO glue(op_packssdw, SUFFIX) (void) +{ + Reg r, *d, *s; + d = (Reg *)((char *)env + PARAM1); + s = (Reg *)((char *)env + PARAM2); + + r.W(0) = satsw(d->L(0)); + r.W(1) = satsw(d->L(1)); +#if SHIFT == 1 + r.W(2) = satsw(d->L(2)); + r.W(3) = satsw(d->L(3)); +#endif + r.W((2 << SHIFT) + 0) = satsw(s->L(0)); + r.W((2 << SHIFT) + 1) = satsw(s->L(1)); +#if SHIFT == 1 + r.W(6) = satsw(s->L(2)); + r.W(7) = satsw(s->L(3)); +#endif + *d = r; +} + +#define UNPCK_OP(base_name, base) \ + \ +void OPPROTO glue(op_punpck ## base_name ## bw, SUFFIX) (void) \ +{ \ + Reg r, *d, *s; \ + d = (Reg *)((char *)env + PARAM1); \ + s = (Reg *)((char *)env + PARAM2); \ + \ + r.B(0) = d->B((base << (SHIFT + 2)) + 0); \ + r.B(1) = s->B((base << (SHIFT + 2)) + 0); \ + r.B(2) = d->B((base << (SHIFT + 2)) + 1); \ + r.B(3) = s->B((base << (SHIFT + 2)) + 1); \ + r.B(4) = d->B((base << (SHIFT + 2)) + 2); \ + r.B(5) = s->B((base << (SHIFT + 2)) + 2); \ + r.B(6) = d->B((base << (SHIFT + 2)) + 3); \ + r.B(7) = s->B((base << (SHIFT + 2)) + 3); \ +XMM_ONLY( \ + r.B(8) = d->B((base << (SHIFT + 2)) + 4); \ + r.B(9) = s->B((base << (SHIFT + 2)) + 4); \ + r.B(10) = d->B((base << (SHIFT + 2)) + 5); \ + r.B(11) = s->B((base << (SHIFT + 2)) + 5); \ + r.B(12) = d->B((base << (SHIFT + 2)) + 6); \ + r.B(13) = s->B((base << (SHIFT + 2)) + 6); \ + r.B(14) = d->B((base << (SHIFT + 2)) + 7); \ + r.B(15) = s->B((base << (SHIFT + 2)) + 7); \ +) \ + *d = r; \ +} \ + \ +void OPPROTO glue(op_punpck ## base_name ## wd, SUFFIX) (void) \ +{ \ + Reg r, *d, *s; \ + d = (Reg *)((char *)env + PARAM1); \ + s = (Reg *)((char *)env + PARAM2); \ + \ + r.W(0) = d->W((base << (SHIFT + 1)) + 0); \ + r.W(1) = s->W((base << (SHIFT + 1)) + 0); \ + r.W(2) = d->W((base << (SHIFT + 1)) + 1); \ + r.W(3) = s->W((base << (SHIFT + 1)) + 1); \ +XMM_ONLY( \ + r.W(4) = d->W((base << (SHIFT + 1)) + 2); \ + r.W(5) = s->W((base << (SHIFT + 1)) + 2); \ + r.W(6) = d->W((base << (SHIFT + 1)) + 3); \ + r.W(7) = s->W((base << (SHIFT + 1)) + 3); \ +) \ + *d = r; \ +} \ + \ +void OPPROTO glue(op_punpck ## base_name ## dq, SUFFIX) (void) \ +{ \ + Reg r, *d, *s; \ + d = (Reg *)((char *)env + PARAM1); \ + s = (Reg *)((char *)env + PARAM2); \ + \ + r.L(0) = d->L((base << SHIFT) + 0); \ + r.L(1) = s->L((base << SHIFT) + 0); \ +XMM_ONLY( \ + r.L(2) = d->L((base << SHIFT) + 1); \ + r.L(3) = s->L((base << SHIFT) + 1); \ +) \ + *d = r; \ +} \ + \ +XMM_ONLY( \ +void OPPROTO glue(op_punpck ## base_name ## qdq, SUFFIX) (void) \ +{ \ + Reg r, *d, *s; \ + d = (Reg *)((char *)env + PARAM1); \ + s = (Reg *)((char *)env + PARAM2); \ + \ + r.Q(0) = d->Q(base); \ + r.Q(1) = s->Q(base); \ + *d = r; \ +} \ +) + +UNPCK_OP(l, 0) +UNPCK_OP(h, 1) + +#undef SHIFT +#undef XMM_ONLY +#undef Reg +#undef B +#undef W +#undef L +#undef Q +#undef SUFFIX diff --git a/tools/ioemu/target-i386/ops_template.h b/tools/ioemu/target-i386/ops_template.h new file mode 100644 index 0000000000..373b77a245 --- /dev/null +++ b/tools/ioemu/target-i386/ops_template.h @@ -0,0 +1,597 @@ +/* + * i386 micro operations (included several times to generate + * different operand sizes) + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#define DATA_BITS (1 << (3 + SHIFT)) +#define SHIFT_MASK (DATA_BITS - 1) +#define SIGN_MASK (((target_ulong)1) << (DATA_BITS - 1)) +#if DATA_BITS <= 32 +#define SHIFT1_MASK 0x1f +#else +#define SHIFT1_MASK 0x3f +#endif + +#if DATA_BITS == 8 +#define SUFFIX b +#define DATA_TYPE uint8_t +#define DATA_STYPE int8_t +#define DATA_MASK 0xff +#elif DATA_BITS == 16 +#define SUFFIX w +#define DATA_TYPE uint16_t +#define DATA_STYPE int16_t +#define DATA_MASK 0xffff +#elif DATA_BITS == 32 +#define SUFFIX l +#define DATA_TYPE uint32_t +#define DATA_STYPE int32_t +#define DATA_MASK 0xffffffff +#elif DATA_BITS == 64 +#define SUFFIX q +#define DATA_TYPE uint64_t +#define DATA_STYPE int64_t +#define DATA_MASK 0xffffffffffffffffULL +#else +#error unhandled operand size +#endif + +/* dynamic flags computation */ + +static int glue(compute_all_add, SUFFIX)(void) +{ + int cf, pf, af, zf, sf, of; + target_long src1, src2; + src1 = CC_SRC; + src2 = CC_DST - CC_SRC; + cf = (DATA_TYPE)CC_DST < (DATA_TYPE)src1; + pf = parity_table[(uint8_t)CC_DST]; + af = (CC_DST ^ src1 ^ src2) & 0x10; + zf = ((DATA_TYPE)CC_DST == 0) << 6; + sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; + of = lshift((src1 ^ src2 ^ -1) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_add, SUFFIX)(void) +{ + int cf; + target_long src1; + src1 = CC_SRC; + cf = (DATA_TYPE)CC_DST < (DATA_TYPE)src1; + return cf; +} + +static int glue(compute_all_adc, SUFFIX)(void) +{ + int cf, pf, af, zf, sf, of; + target_long src1, src2; + src1 = CC_SRC; + src2 = CC_DST - CC_SRC - 1; + cf = (DATA_TYPE)CC_DST <= (DATA_TYPE)src1; + pf = parity_table[(uint8_t)CC_DST]; + af = (CC_DST ^ src1 ^ src2) & 0x10; + zf = ((DATA_TYPE)CC_DST == 0) << 6; + sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; + of = lshift((src1 ^ src2 ^ -1) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_adc, SUFFIX)(void) +{ + int cf; + target_long src1; + src1 = CC_SRC; + cf = (DATA_TYPE)CC_DST <= (DATA_TYPE)src1; + return cf; +} + +static int glue(compute_all_sub, SUFFIX)(void) +{ + int cf, pf, af, zf, sf, of; + target_long src1, src2; + src1 = CC_DST + CC_SRC; + src2 = CC_SRC; + cf = (DATA_TYPE)src1 < (DATA_TYPE)src2; + pf = parity_table[(uint8_t)CC_DST]; + af = (CC_DST ^ src1 ^ src2) & 0x10; + zf = ((DATA_TYPE)CC_DST == 0) << 6; + sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; + of = lshift((src1 ^ src2) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_sub, SUFFIX)(void) +{ + int cf; + target_long src1, src2; + src1 = CC_DST + CC_SRC; + src2 = CC_SRC; + cf = (DATA_TYPE)src1 < (DATA_TYPE)src2; + return cf; +} + +static int glue(compute_all_sbb, SUFFIX)(void) +{ + int cf, pf, af, zf, sf, of; + target_long src1, src2; + src1 = CC_DST + CC_SRC + 1; + src2 = CC_SRC; + cf = (DATA_TYPE)src1 <= (DATA_TYPE)src2; + pf = parity_table[(uint8_t)CC_DST]; + af = (CC_DST ^ src1 ^ src2) & 0x10; + zf = ((DATA_TYPE)CC_DST == 0) << 6; + sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; + of = lshift((src1 ^ src2) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_sbb, SUFFIX)(void) +{ + int cf; + target_long src1, src2; + src1 = CC_DST + CC_SRC + 1; + src2 = CC_SRC; + cf = (DATA_TYPE)src1 <= (DATA_TYPE)src2; + return cf; +} + +static int glue(compute_all_logic, SUFFIX)(void) +{ + int cf, pf, af, zf, sf, of; + cf = 0; + pf = parity_table[(uint8_t)CC_DST]; + af = 0; + zf = ((DATA_TYPE)CC_DST == 0) << 6; + sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; + of = 0; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_logic, SUFFIX)(void) +{ + return 0; +} + +static int glue(compute_all_inc, SUFFIX)(void) +{ + int cf, pf, af, zf, sf, of; + target_long src1, src2; + src1 = CC_DST - 1; + src2 = 1; + cf = CC_SRC; + pf = parity_table[(uint8_t)CC_DST]; + af = (CC_DST ^ src1 ^ src2) & 0x10; + zf = ((DATA_TYPE)CC_DST == 0) << 6; + sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; + of = ((CC_DST & DATA_MASK) == SIGN_MASK) << 11; + return cf | pf | af | zf | sf | of; +} + +#if DATA_BITS == 32 +static int glue(compute_c_inc, SUFFIX)(void) +{ + return CC_SRC; +} +#endif + +static int glue(compute_all_dec, SUFFIX)(void) +{ + int cf, pf, af, zf, sf, of; + target_long src1, src2; + src1 = CC_DST + 1; + src2 = 1; + cf = CC_SRC; + pf = parity_table[(uint8_t)CC_DST]; + af = (CC_DST ^ src1 ^ src2) & 0x10; + zf = ((DATA_TYPE)CC_DST == 0) << 6; + sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; + of = ((CC_DST & DATA_MASK) == ((target_ulong)SIGN_MASK - 1)) << 11; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_all_shl, SUFFIX)(void) +{ + int cf, pf, af, zf, sf, of; + cf = (CC_SRC >> (DATA_BITS - 1)) & CC_C; + pf = parity_table[(uint8_t)CC_DST]; + af = 0; /* undefined */ + zf = ((DATA_TYPE)CC_DST == 0) << 6; + sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; + /* of is defined if shift count == 1 */ + of = lshift(CC_SRC ^ CC_DST, 12 - DATA_BITS) & CC_O; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_shl, SUFFIX)(void) +{ + return (CC_SRC >> (DATA_BITS - 1)) & CC_C; +} + +#if DATA_BITS == 32 +static int glue(compute_c_sar, SUFFIX)(void) +{ + return CC_SRC & 1; +} +#endif + +static int glue(compute_all_sar, SUFFIX)(void) +{ + int cf, pf, af, zf, sf, of; + cf = CC_SRC & 1; + pf = parity_table[(uint8_t)CC_DST]; + af = 0; /* undefined */ + zf = ((DATA_TYPE)CC_DST == 0) << 6; + sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; + /* of is defined if shift count == 1 */ + of = lshift(CC_SRC ^ CC_DST, 12 - DATA_BITS) & CC_O; + return cf | pf | af | zf | sf | of; +} + +#if DATA_BITS == 32 +static int glue(compute_c_mul, SUFFIX)(void) +{ + int cf; + cf = (CC_SRC != 0); + return cf; +} +#endif + +/* NOTE: we compute the flags like the P4. On olders CPUs, only OF and + CF are modified and it is slower to do that. */ +static int glue(compute_all_mul, SUFFIX)(void) +{ + int cf, pf, af, zf, sf, of; + cf = (CC_SRC != 0); + pf = parity_table[(uint8_t)CC_DST]; + af = 0; /* undefined */ + zf = ((DATA_TYPE)CC_DST == 0) << 6; + sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; + of = cf << 11; + return cf | pf | af | zf | sf | of; +} + +/* various optimized jumps cases */ + +void OPPROTO glue(op_jb_sub, SUFFIX)(void) +{ + target_long src1, src2; + src1 = CC_DST + CC_SRC; + src2 = CC_SRC; + + if ((DATA_TYPE)src1 < (DATA_TYPE)src2) + GOTO_LABEL_PARAM(1); + FORCE_RET(); +} + +void OPPROTO glue(op_jz_sub, SUFFIX)(void) +{ + if ((DATA_TYPE)CC_DST == 0) + GOTO_LABEL_PARAM(1); + FORCE_RET(); +} + +void OPPROTO glue(op_jnz_sub, SUFFIX)(void) +{ + if ((DATA_TYPE)CC_DST != 0) + GOTO_LABEL_PARAM(1); + FORCE_RET(); +} + +void OPPROTO glue(op_jbe_sub, SUFFIX)(void) +{ + target_long src1, src2; + src1 = CC_DST + CC_SRC; + src2 = CC_SRC; + + if ((DATA_TYPE)src1 <= (DATA_TYPE)src2) + GOTO_LABEL_PARAM(1); + FORCE_RET(); +} + +void OPPROTO glue(op_js_sub, SUFFIX)(void) +{ + if (CC_DST & SIGN_MASK) + GOTO_LABEL_PARAM(1); + FORCE_RET(); +} + +void OPPROTO glue(op_jl_sub, SUFFIX)(void) +{ + target_long src1, src2; + src1 = CC_DST + CC_SRC; + src2 = CC_SRC; + + if ((DATA_STYPE)src1 < (DATA_STYPE)src2) + GOTO_LABEL_PARAM(1); + FORCE_RET(); +} + +void OPPROTO glue(op_jle_sub, SUFFIX)(void) +{ + target_long src1, src2; + src1 = CC_DST + CC_SRC; + src2 = CC_SRC; + + if ((DATA_STYPE)src1 <= (DATA_STYPE)src2) + GOTO_LABEL_PARAM(1); + FORCE_RET(); +} + +/* oldies */ + +#if DATA_BITS >= 16 + +void OPPROTO glue(op_loopnz, SUFFIX)(void) +{ + if ((DATA_TYPE)ECX != 0 && !(T0 & CC_Z)) + GOTO_LABEL_PARAM(1); + FORCE_RET(); +} + +void OPPROTO glue(op_loopz, SUFFIX)(void) +{ + if ((DATA_TYPE)ECX != 0 && (T0 & CC_Z)) + GOTO_LABEL_PARAM(1); + FORCE_RET(); +} + +void OPPROTO glue(op_jz_ecx, SUFFIX)(void) +{ + if ((DATA_TYPE)ECX == 0) + GOTO_LABEL_PARAM(1); + FORCE_RET(); +} + +void OPPROTO glue(op_jnz_ecx, SUFFIX)(void) +{ + if ((DATA_TYPE)ECX != 0) + GOTO_LABEL_PARAM(1); + FORCE_RET(); +} + +#endif + +/* various optimized set cases */ + +void OPPROTO glue(op_setb_T0_sub, SUFFIX)(void) +{ + target_long src1, src2; + src1 = CC_DST + CC_SRC; + src2 = CC_SRC; + + T0 = ((DATA_TYPE)src1 < (DATA_TYPE)src2); +} + +void OPPROTO glue(op_setz_T0_sub, SUFFIX)(void) +{ + T0 = ((DATA_TYPE)CC_DST == 0); +} + +void OPPROTO glue(op_setbe_T0_sub, SUFFIX)(void) +{ + target_long src1, src2; + src1 = CC_DST + CC_SRC; + src2 = CC_SRC; + + T0 = ((DATA_TYPE)src1 <= (DATA_TYPE)src2); +} + +void OPPROTO glue(op_sets_T0_sub, SUFFIX)(void) +{ + T0 = lshift(CC_DST, -(DATA_BITS - 1)) & 1; +} + +void OPPROTO glue(op_setl_T0_sub, SUFFIX)(void) +{ + target_long src1, src2; + src1 = CC_DST + CC_SRC; + src2 = CC_SRC; + + T0 = ((DATA_STYPE)src1 < (DATA_STYPE)src2); +} + +void OPPROTO glue(op_setle_T0_sub, SUFFIX)(void) +{ + target_long src1, src2; + src1 = CC_DST + CC_SRC; + src2 = CC_SRC; + + T0 = ((DATA_STYPE)src1 <= (DATA_STYPE)src2); +} + +/* shifts */ + +void OPPROTO glue(glue(op_shl, SUFFIX), _T0_T1)(void) +{ + int count; + count = T1 & SHIFT1_MASK; + T0 = T0 << count; + FORCE_RET(); +} + +void OPPROTO glue(glue(op_shr, SUFFIX), _T0_T1)(void) +{ + int count; + count = T1 & SHIFT1_MASK; + T0 &= DATA_MASK; + T0 = T0 >> count; + FORCE_RET(); +} + +void OPPROTO glue(glue(op_sar, SUFFIX), _T0_T1)(void) +{ + int count; + target_long src; + + count = T1 & SHIFT1_MASK; + src = (DATA_STYPE)T0; + T0 = src >> count; + FORCE_RET(); +} + +#undef MEM_WRITE +#include "ops_template_mem.h" + +#define MEM_WRITE 0 +#include "ops_template_mem.h" + +#if !defined(CONFIG_USER_ONLY) +#define MEM_WRITE 1 +#include "ops_template_mem.h" + +#define MEM_WRITE 2 +#include "ops_template_mem.h" +#endif + +/* bit operations */ +#if DATA_BITS >= 16 + +void OPPROTO glue(glue(op_bt, SUFFIX), _T0_T1_cc)(void) +{ + int count; + count = T1 & SHIFT_MASK; + CC_SRC = T0 >> count; +} + +void OPPROTO glue(glue(op_bts, SUFFIX), _T0_T1_cc)(void) +{ + int count; + count = T1 & SHIFT_MASK; + T1 = T0 >> count; + T0 |= (((target_long)1) << count); +} + +void OPPROTO glue(glue(op_btr, SUFFIX), _T0_T1_cc)(void) +{ + int count; + count = T1 & SHIFT_MASK; + T1 = T0 >> count; + T0 &= ~(((target_long)1) << count); +} + +void OPPROTO glue(glue(op_btc, SUFFIX), _T0_T1_cc)(void) +{ + int count; + count = T1 & SHIFT_MASK; + T1 = T0 >> count; + T0 ^= (((target_long)1) << count); +} + +void OPPROTO glue(glue(op_add_bit, SUFFIX), _A0_T1)(void) +{ + A0 += ((DATA_STYPE)T1 >> (3 + SHIFT)) << SHIFT; +} + +void OPPROTO glue(glue(op_bsf, SUFFIX), _T0_cc)(void) +{ + int count; + target_long res; + + res = T0 & DATA_MASK; + if (res != 0) { + count = 0; + while ((res & 1) == 0) { + count++; + res >>= 1; + } + T1 = count; + CC_DST = 1; /* ZF = 0 */ + } else { + CC_DST = 0; /* ZF = 1 */ + } + FORCE_RET(); +} + +void OPPROTO glue(glue(op_bsr, SUFFIX), _T0_cc)(void) +{ + int count; + target_long res; + + res = T0 & DATA_MASK; + if (res != 0) { + count = DATA_BITS - 1; + while ((res & SIGN_MASK) == 0) { + count--; + res <<= 1; + } + T1 = count; + CC_DST = 1; /* ZF = 0 */ + } else { + CC_DST = 0; /* ZF = 1 */ + } + FORCE_RET(); +} + +#endif + +#if DATA_BITS == 32 +void OPPROTO op_update_bt_cc(void) +{ + CC_SRC = T1; +} +#endif + +/* string operations */ + +void OPPROTO glue(op_movl_T0_Dshift, SUFFIX)(void) +{ + T0 = DF << SHIFT; +} + +/* port I/O */ +#if DATA_BITS <= 32 +void OPPROTO glue(glue(op_out, SUFFIX), _T0_T1)(void) +{ + glue(cpu_out, SUFFIX)(env, T0, T1 & DATA_MASK); +} + +void OPPROTO glue(glue(op_in, SUFFIX), _T0_T1)(void) +{ + T1 = glue(cpu_in, SUFFIX)(env, T0); +} + +void OPPROTO glue(glue(op_in, SUFFIX), _DX_T0)(void) +{ + T0 = glue(cpu_in, SUFFIX)(env, EDX & 0xffff); +} + +void OPPROTO glue(glue(op_out, SUFFIX), _DX_T0)(void) +{ + glue(cpu_out, SUFFIX)(env, EDX & 0xffff, T0); +} + +void OPPROTO glue(glue(op_check_io, SUFFIX), _T0)(void) +{ + glue(glue(check_io, SUFFIX), _T0)(); +} + +void OPPROTO glue(glue(op_check_io, SUFFIX), _DX)(void) +{ + glue(glue(check_io, SUFFIX), _DX)(); +} +#endif + +#undef DATA_BITS +#undef SHIFT_MASK +#undef SHIFT1_MASK +#undef SIGN_MASK +#undef DATA_TYPE +#undef DATA_STYPE +#undef DATA_MASK +#undef SUFFIX diff --git a/tools/ioemu/target-i386/ops_template_mem.h b/tools/ioemu/target-i386/ops_template_mem.h new file mode 100644 index 0000000000..9f72a8c965 --- /dev/null +++ b/tools/ioemu/target-i386/ops_template_mem.h @@ -0,0 +1,483 @@ +/* + * i386 micro operations (included several times to generate + * different operand sizes) + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifdef MEM_WRITE + +#if MEM_WRITE == 0 + +#if DATA_BITS == 8 +#define MEM_SUFFIX b_raw +#elif DATA_BITS == 16 +#define MEM_SUFFIX w_raw +#elif DATA_BITS == 32 +#define MEM_SUFFIX l_raw +#elif DATA_BITS == 64 +#define MEM_SUFFIX q_raw +#endif + +#elif MEM_WRITE == 1 + +#if DATA_BITS == 8 +#define MEM_SUFFIX b_kernel +#elif DATA_BITS == 16 +#define MEM_SUFFIX w_kernel +#elif DATA_BITS == 32 +#define MEM_SUFFIX l_kernel +#elif DATA_BITS == 64 +#define MEM_SUFFIX q_kernel +#endif + +#elif MEM_WRITE == 2 + +#if DATA_BITS == 8 +#define MEM_SUFFIX b_user +#elif DATA_BITS == 16 +#define MEM_SUFFIX w_user +#elif DATA_BITS == 32 +#define MEM_SUFFIX l_user +#elif DATA_BITS == 64 +#define MEM_SUFFIX q_user +#endif + +#else + +#error invalid MEM_WRITE + +#endif + +#else + +#define MEM_SUFFIX SUFFIX + +#endif + +void OPPROTO glue(glue(op_rol, MEM_SUFFIX), _T0_T1_cc)(void) +{ + int count; + target_long src; + + if (T1 & SHIFT1_MASK) { + count = T1 & SHIFT_MASK; + src = T0; + T0 &= DATA_MASK; + T0 = (T0 << count) | (T0 >> (DATA_BITS - count)); +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#else + /* gcc 3.2 workaround. This is really a bug in gcc. */ + asm volatile("" : : "r" (T0)); +#endif + CC_SRC = (cc_table[CC_OP].compute_all() & ~(CC_O | CC_C)) | + (lshift(src ^ T0, 11 - (DATA_BITS - 1)) & CC_O) | + (T0 & CC_C); + CC_OP = CC_OP_EFLAGS; + } + FORCE_RET(); +} + +void OPPROTO glue(glue(op_ror, MEM_SUFFIX), _T0_T1_cc)(void) +{ + int count; + target_long src; + + if (T1 & SHIFT1_MASK) { + count = T1 & SHIFT_MASK; + src = T0; + T0 &= DATA_MASK; + T0 = (T0 >> count) | (T0 << (DATA_BITS - count)); +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#else + /* gcc 3.2 workaround. This is really a bug in gcc. */ + asm volatile("" : : "r" (T0)); +#endif + CC_SRC = (cc_table[CC_OP].compute_all() & ~(CC_O | CC_C)) | + (lshift(src ^ T0, 11 - (DATA_BITS - 1)) & CC_O) | + ((T0 >> (DATA_BITS - 1)) & CC_C); + CC_OP = CC_OP_EFLAGS; + } + FORCE_RET(); +} + +void OPPROTO glue(glue(op_rol, MEM_SUFFIX), _T0_T1)(void) +{ + int count; + count = T1 & SHIFT_MASK; + if (count) { + T0 &= DATA_MASK; + T0 = (T0 << count) | (T0 >> (DATA_BITS - count)); +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + } + FORCE_RET(); +} + +void OPPROTO glue(glue(op_ror, MEM_SUFFIX), _T0_T1)(void) +{ + int count; + count = T1 & SHIFT_MASK; + if (count) { + T0 &= DATA_MASK; + T0 = (T0 >> count) | (T0 << (DATA_BITS - count)); +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + } + FORCE_RET(); +} + +void OPPROTO glue(glue(op_rcl, MEM_SUFFIX), _T0_T1_cc)(void) +{ + int count, eflags; + target_ulong src; + target_long res; + + count = T1 & SHIFT1_MASK; +#if DATA_BITS == 16 + count = rclw_table[count]; +#elif DATA_BITS == 8 + count = rclb_table[count]; +#endif + if (count) { + eflags = cc_table[CC_OP].compute_all(); + T0 &= DATA_MASK; + src = T0; + res = (T0 << count) | ((target_ulong)(eflags & CC_C) << (count - 1)); + if (count > 1) + res |= T0 >> (DATA_BITS + 1 - count); + T0 = res; +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = (eflags & ~(CC_C | CC_O)) | + (lshift(src ^ T0, 11 - (DATA_BITS - 1)) & CC_O) | + ((src >> (DATA_BITS - count)) & CC_C); + CC_OP = CC_OP_EFLAGS; + } + FORCE_RET(); +} + +void OPPROTO glue(glue(op_rcr, MEM_SUFFIX), _T0_T1_cc)(void) +{ + int count, eflags; + target_ulong src; + target_long res; + + count = T1 & SHIFT1_MASK; +#if DATA_BITS == 16 + count = rclw_table[count]; +#elif DATA_BITS == 8 + count = rclb_table[count]; +#endif + if (count) { + eflags = cc_table[CC_OP].compute_all(); + T0 &= DATA_MASK; + src = T0; + res = (T0 >> count) | ((target_ulong)(eflags & CC_C) << (DATA_BITS - count)); + if (count > 1) + res |= T0 << (DATA_BITS + 1 - count); + T0 = res; +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = (eflags & ~(CC_C | CC_O)) | + (lshift(src ^ T0, 11 - (DATA_BITS - 1)) & CC_O) | + ((src >> (count - 1)) & CC_C); + CC_OP = CC_OP_EFLAGS; + } + FORCE_RET(); +} + +void OPPROTO glue(glue(op_shl, MEM_SUFFIX), _T0_T1_cc)(void) +{ + int count; + target_long src; + + count = T1 & SHIFT1_MASK; + if (count) { + src = (DATA_TYPE)T0 << (count - 1); + T0 = T0 << count; +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = src; + CC_DST = T0; + CC_OP = CC_OP_SHLB + SHIFT; + } + FORCE_RET(); +} + +void OPPROTO glue(glue(op_shr, MEM_SUFFIX), _T0_T1_cc)(void) +{ + int count; + target_long src; + + count = T1 & SHIFT1_MASK; + if (count) { + T0 &= DATA_MASK; + src = T0 >> (count - 1); + T0 = T0 >> count; +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = src; + CC_DST = T0; + CC_OP = CC_OP_SARB + SHIFT; + } + FORCE_RET(); +} + +void OPPROTO glue(glue(op_sar, MEM_SUFFIX), _T0_T1_cc)(void) +{ + int count; + target_long src; + + count = T1 & SHIFT1_MASK; + if (count) { + src = (DATA_STYPE)T0; + T0 = src >> count; + src = src >> (count - 1); +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = src; + CC_DST = T0; + CC_OP = CC_OP_SARB + SHIFT; + } + FORCE_RET(); +} + +#if DATA_BITS == 16 +/* XXX: overflow flag might be incorrect in some cases in shldw */ +void OPPROTO glue(glue(op_shld, MEM_SUFFIX), _T0_T1_im_cc)(void) +{ + int count; + unsigned int res, tmp; + count = PARAM1; + T1 &= 0xffff; + res = T1 | (T0 << 16); + tmp = res >> (32 - count); + res <<= count; + if (count > 16) + res |= T1 << (count - 16); + T0 = res >> 16; +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = tmp; + CC_DST = T0; +} + +void OPPROTO glue(glue(op_shld, MEM_SUFFIX), _T0_T1_ECX_cc)(void) +{ + int count; + unsigned int res, tmp; + count = ECX & 0x1f; + if (count) { + T1 &= 0xffff; + res = T1 | (T0 << 16); + tmp = res >> (32 - count); + res <<= count; + if (count > 16) + res |= T1 << (count - 16); + T0 = res >> 16; +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = tmp; + CC_DST = T0; + CC_OP = CC_OP_SARB + SHIFT; + } + FORCE_RET(); +} + +void OPPROTO glue(glue(op_shrd, MEM_SUFFIX), _T0_T1_im_cc)(void) +{ + int count; + unsigned int res, tmp; + + count = PARAM1; + res = (T0 & 0xffff) | (T1 << 16); + tmp = res >> (count - 1); + res >>= count; + if (count > 16) + res |= T1 << (32 - count); + T0 = res; +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = tmp; + CC_DST = T0; +} + + +void OPPROTO glue(glue(op_shrd, MEM_SUFFIX), _T0_T1_ECX_cc)(void) +{ + int count; + unsigned int res, tmp; + + count = ECX & 0x1f; + if (count) { + res = (T0 & 0xffff) | (T1 << 16); + tmp = res >> (count - 1); + res >>= count; + if (count > 16) + res |= T1 << (32 - count); + T0 = res; +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = tmp; + CC_DST = T0; + CC_OP = CC_OP_SARB + SHIFT; + } + FORCE_RET(); +} +#endif + +#if DATA_BITS >= 32 +void OPPROTO glue(glue(op_shld, MEM_SUFFIX), _T0_T1_im_cc)(void) +{ + int count; + target_long tmp; + + count = PARAM1; + T0 &= DATA_MASK; + T1 &= DATA_MASK; + tmp = T0 << (count - 1); + T0 = (T0 << count) | (T1 >> (DATA_BITS - count)); +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = tmp; + CC_DST = T0; +} + +void OPPROTO glue(glue(op_shld, MEM_SUFFIX), _T0_T1_ECX_cc)(void) +{ + int count; + target_long tmp; + + count = ECX & SHIFT1_MASK; + if (count) { + T0 &= DATA_MASK; + T1 &= DATA_MASK; + tmp = T0 << (count - 1); + T0 = (T0 << count) | (T1 >> (DATA_BITS - count)); +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = tmp; + CC_DST = T0; + CC_OP = CC_OP_SHLB + SHIFT; + } + FORCE_RET(); +} + +void OPPROTO glue(glue(op_shrd, MEM_SUFFIX), _T0_T1_im_cc)(void) +{ + int count; + target_long tmp; + + count = PARAM1; + T0 &= DATA_MASK; + T1 &= DATA_MASK; + tmp = T0 >> (count - 1); + T0 = (T0 >> count) | (T1 << (DATA_BITS - count)); +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = tmp; + CC_DST = T0; +} + + +void OPPROTO glue(glue(op_shrd, MEM_SUFFIX), _T0_T1_ECX_cc)(void) +{ + int count; + target_long tmp; + + count = ECX & SHIFT1_MASK; + if (count) { + T0 &= DATA_MASK; + T1 &= DATA_MASK; + tmp = T0 >> (count - 1); + T0 = (T0 >> count) | (T1 << (DATA_BITS - count)); +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = tmp; + CC_DST = T0; + CC_OP = CC_OP_SARB + SHIFT; + } + FORCE_RET(); +} +#endif + +/* carry add/sub (we only need to set CC_OP differently) */ + +void OPPROTO glue(glue(op_adc, MEM_SUFFIX), _T0_T1_cc)(void) +{ + int cf; + cf = cc_table[CC_OP].compute_c(); + T0 = T0 + T1 + cf; +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = T1; + CC_DST = T0; + CC_OP = CC_OP_ADDB + SHIFT + cf * 4; +} + +void OPPROTO glue(glue(op_sbb, MEM_SUFFIX), _T0_T1_cc)(void) +{ + int cf; + cf = cc_table[CC_OP].compute_c(); + T0 = T0 - T1 - cf; +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + CC_SRC = T1; + CC_DST = T0; + CC_OP = CC_OP_SUBB + SHIFT + cf * 4; +} + +void OPPROTO glue(glue(op_cmpxchg, MEM_SUFFIX), _T0_T1_EAX_cc)(void) +{ + target_ulong src, dst; + + src = T0; + dst = EAX - T0; + if ((DATA_TYPE)dst == 0) { + T0 = T1; +#ifdef MEM_WRITE + glue(st, MEM_SUFFIX)(A0, T0); +#endif + } else { + EAX = (EAX & ~DATA_MASK) | (T0 & DATA_MASK); + } + CC_SRC = src; + CC_DST = dst; + FORCE_RET(); +} + +#undef MEM_SUFFIX +#undef MEM_WRITE diff --git a/tools/ioemu/target-i386/translate-copy.c b/tools/ioemu/target-i386/translate-copy.c new file mode 100644 index 0000000000..cf8bd5ab3f --- /dev/null +++ b/tools/ioemu/target-i386/translate-copy.c @@ -0,0 +1,1323 @@ +/* + * i386 on i386 translation + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "exec-all.h" +#include "disas.h" + +#ifdef USE_CODE_COPY + +#include +#include +#include + +extern char exec_loop; + +/* operand size */ +enum { + OT_BYTE = 0, + OT_WORD, + OT_LONG, + OT_QUAD, +}; + +#define PREFIX_REPZ 0x01 +#define PREFIX_REPNZ 0x02 +#define PREFIX_LOCK 0x04 +#define PREFIX_DATA 0x08 +#define PREFIX_ADR 0x10 + +typedef struct DisasContext { + /* current insn context */ + int override; /* -1 if no override */ + int prefix; + int aflag, dflag; + target_ulong pc; /* pc = eip + cs_base */ + int is_jmp; /* 1 = means jump (stop translation), 2 means CPU + static state change (stop translation) */ + /* code output */ + uint8_t *gen_code_ptr; + uint8_t *gen_code_start; + + /* current block context */ + target_ulong cs_base; /* base of CS segment */ + int pe; /* protected mode */ + int code32; /* 32 bit code segment */ + int f_st; /* currently unused */ + int vm86; /* vm86 mode */ + int cpl; + int iopl; + int flags; + struct TranslationBlock *tb; +} DisasContext; + +#define CPU_FIELD_OFFSET(field) offsetof(CPUState, field) + +#define CPU_SEG 0x64 /* fs override */ + +static inline void gb(DisasContext *s, uint32_t val) +{ + *s->gen_code_ptr++ = val; +} + +static inline void gw(DisasContext *s, uint32_t val) +{ + *s->gen_code_ptr++ = val; + *s->gen_code_ptr++ = val >> 8; +} + +static inline void gl(DisasContext *s, uint32_t val) +{ + *s->gen_code_ptr++ = val; + *s->gen_code_ptr++ = val >> 8; + *s->gen_code_ptr++ = val >> 16; + *s->gen_code_ptr++ = val >> 24; +} + +static inline void gjmp(DisasContext *s, long val) +{ + gb(s, 0xe9); /* jmp */ + gl(s, val - (long)(s->gen_code_ptr + 4)); +} + +static inline void gen_movl_addr_im(DisasContext *s, + uint32_t addr, uint32_t val) +{ + gb(s, CPU_SEG); /* seg movl im, addr */ + gb(s, 0xc7); + gb(s, 0x05); + gl(s, addr); + gl(s, val); +} + +static inline void gen_movw_addr_im(DisasContext *s, + uint32_t addr, uint32_t val) +{ + gb(s, CPU_SEG); /* seg movl im, addr */ + gb(s, 0x66); + gb(s, 0xc7); + gb(s, 0x05); + gl(s, addr); + gw(s, val); +} + + +static void gen_jmp(DisasContext *s, uint32_t target_eip) +{ + TranslationBlock *tb = s->tb; + + gb(s, 0xe9); /* jmp */ + tb->tb_jmp_offset[0] = s->gen_code_ptr - s->gen_code_start; + gl(s, 0); + + tb->tb_next_offset[0] = s->gen_code_ptr - s->gen_code_start; + gen_movl_addr_im(s, CPU_FIELD_OFFSET(eip), target_eip); + gen_movl_addr_im(s, CPU_FIELD_OFFSET(tmp0), (uint32_t)tb); + gjmp(s, (long)&exec_loop); + + s->is_jmp = 1; +} + +static void gen_jcc(DisasContext *s, int op, + uint32_t target_eip, uint32_t next_eip) +{ + TranslationBlock *tb = s->tb; + + gb(s, 0x0f); /* jcc */ + gb(s, 0x80 + op); + tb->tb_jmp_offset[0] = s->gen_code_ptr - s->gen_code_start; + gl(s, 0); + gb(s, 0xe9); /* jmp */ + tb->tb_jmp_offset[1] = s->gen_code_ptr - s->gen_code_start; + gl(s, 0); + + tb->tb_next_offset[0] = s->gen_code_ptr - s->gen_code_start; + gen_movl_addr_im(s, CPU_FIELD_OFFSET(eip), target_eip); + gen_movl_addr_im(s, CPU_FIELD_OFFSET(tmp0), (uint32_t)tb); + gjmp(s, (long)&exec_loop); + + tb->tb_next_offset[1] = s->gen_code_ptr - s->gen_code_start; + gen_movl_addr_im(s, CPU_FIELD_OFFSET(eip), next_eip); + gen_movl_addr_im(s, CPU_FIELD_OFFSET(tmp0), (uint32_t)tb | 1); + gjmp(s, (long)&exec_loop); + + s->is_jmp = 1; +} + +static void gen_eob(DisasContext *s) +{ + gen_movl_addr_im(s, CPU_FIELD_OFFSET(tmp0), 0); + gjmp(s, (long)&exec_loop); + + s->is_jmp = 1; +} + +static inline void gen_lea_modrm(DisasContext *s, int modrm) +{ + int havesib; + int base, disp; + int index; + int scale; + int mod, rm, code; + + mod = (modrm >> 6) & 3; + rm = modrm & 7; + + if (s->aflag) { + + havesib = 0; + base = rm; + index = 0; + scale = 0; + + if (base == 4) { + havesib = 1; + code = ldub_code(s->pc++); + scale = (code >> 6) & 3; + index = (code >> 3) & 7; + base = code & 7; + } + + switch (mod) { + case 0: + if (base == 5) { + base = -1; + disp = ldl_code(s->pc); + s->pc += 4; + } else { + disp = 0; + } + break; + case 1: + disp = (int8_t)ldub_code(s->pc++); + break; + default: + case 2: + disp = ldl_code(s->pc); + s->pc += 4; + break; + } + + } else { + switch (mod) { + case 0: + if (rm == 6) { + disp = lduw_code(s->pc); + s->pc += 2; + } else { + disp = 0; + } + break; + case 1: + disp = (int8_t)ldub_code(s->pc++); + break; + default: + case 2: + disp = lduw_code(s->pc); + s->pc += 2; + break; + } + } +} + +static inline void parse_modrm(DisasContext *s, int modrm) +{ + if ((modrm & 0xc0) != 0xc0) + gen_lea_modrm(s, modrm); +} + +static inline uint32_t insn_get(DisasContext *s, int ot) +{ + uint32_t ret; + + switch(ot) { + case OT_BYTE: + ret = ldub_code(s->pc); + s->pc++; + break; + case OT_WORD: + ret = lduw_code(s->pc); + s->pc += 2; + break; + default: + case OT_LONG: + ret = ldl_code(s->pc); + s->pc += 4; + break; + } + return ret; +} + +/* convert one instruction. s->is_jmp is set if the translation must + be stopped. */ +static int disas_insn(DisasContext *s) +{ + target_ulong pc_start, pc_tmp, pc_start_insn; + int b, prefixes, aflag, dflag, next_eip, val; + int ot; + int modrm, mod, op, rm; + + pc_start = s->pc; + prefixes = 0; + aflag = s->code32; + dflag = s->code32; + s->override = -1; + next_byte: + b = ldub_code(s->pc); + s->pc++; + /* check prefixes */ + switch (b) { + case 0xf3: + prefixes |= PREFIX_REPZ; + goto next_byte; + case 0xf2: + prefixes |= PREFIX_REPNZ; + goto next_byte; + case 0xf0: + prefixes |= PREFIX_LOCK; + goto next_byte; + case 0x2e: + s->override = R_CS; + goto next_byte; + case 0x36: + s->override = R_SS; + goto next_byte; + case 0x3e: + s->override = R_DS; + goto next_byte; + case 0x26: + s->override = R_ES; + goto next_byte; + case 0x64: + s->override = R_FS; + goto next_byte; + case 0x65: + s->override = R_GS; + goto next_byte; + case 0x66: + prefixes |= PREFIX_DATA; + goto next_byte; + case 0x67: + prefixes |= PREFIX_ADR; + goto next_byte; + } + + if (prefixes & PREFIX_DATA) + dflag ^= 1; + if (prefixes & PREFIX_ADR) + aflag ^= 1; + + s->prefix = prefixes; + s->aflag = aflag; + s->dflag = dflag; + + /* lock generation */ + if (prefixes & PREFIX_LOCK) + goto unsupported_op; + if (s->override == R_FS || s->override == R_GS || s->override == R_CS) + goto unsupported_op; + + pc_start_insn = s->pc - 1; + /* now check op code */ + reswitch: + switch(b) { + case 0x0f: + /**************************/ + /* extended op code */ + b = ldub_code(s->pc++) | 0x100; + goto reswitch; + + /**************************/ + /* arith & logic */ + case 0x00 ... 0x05: + case 0x08 ... 0x0d: + case 0x10 ... 0x15: + case 0x18 ... 0x1d: + case 0x20 ... 0x25: + case 0x28 ... 0x2d: + case 0x30 ... 0x35: + case 0x38 ... 0x3d: + { + int f; + f = (b >> 1) & 3; + + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + + switch(f) { + case 0: /* OP Ev, Gv */ + modrm = ldub_code(s->pc++); + parse_modrm(s, modrm); + break; + case 1: /* OP Gv, Ev */ + modrm = ldub_code(s->pc++); + parse_modrm(s, modrm); + break; + case 2: /* OP A, Iv */ + insn_get(s, ot); + break; + } + } + break; + + case 0x80: /* GRP1 */ + case 0x81: + case 0x82: + case 0x83: + { + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + + modrm = ldub_code(s->pc++); + parse_modrm(s, modrm); + + switch(b) { + default: + case 0x80: + case 0x81: + case 0x82: + insn_get(s, ot); + break; + case 0x83: + insn_get(s, OT_BYTE); + break; + } + } + break; + + /**************************/ + /* inc, dec, and other misc arith */ + case 0x40 ... 0x47: /* inc Gv */ + break; + case 0x48 ... 0x4f: /* dec Gv */ + break; + case 0xf6: /* GRP3 */ + case 0xf7: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + + modrm = ldub_code(s->pc++); + op = (modrm >> 3) & 7; + parse_modrm(s, modrm); + + switch(op) { + case 0: /* test */ + insn_get(s, ot); + break; + case 2: /* not */ + break; + case 3: /* neg */ + break; + case 4: /* mul */ + break; + case 5: /* imul */ + break; + case 6: /* div */ + break; + case 7: /* idiv */ + break; + default: + goto illegal_op; + } + break; + + case 0xfe: /* GRP4 */ + case 0xff: /* GRP5 */ + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + op = (modrm >> 3) & 7; + if (op >= 2 && b == 0xfe) { + goto illegal_op; + } + pc_tmp = s->pc; + parse_modrm(s, modrm); + + switch(op) { + case 0: /* inc Ev */ + break; + case 1: /* dec Ev */ + break; + case 2: /* call Ev */ + /* XXX: optimize and handle MEM exceptions specifically + fs movl %eax, regs[0] + movl Ev, %eax + pushl next_eip + fs movl %eax, eip + */ + goto unsupported_op; + case 3: /* lcall Ev */ + goto unsupported_op; + case 4: /* jmp Ev */ + /* XXX: optimize and handle MEM exceptions specifically + fs movl %eax, regs[0] + movl Ev, %eax + fs movl %eax, eip + */ + goto unsupported_op; + case 5: /* ljmp Ev */ + goto unsupported_op; + case 6: /* push Ev */ + break; + default: + goto illegal_op; + } + break; + case 0xa8: /* test eAX, Iv */ + case 0xa9: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + insn_get(s, ot); + break; + + case 0x98: /* CWDE/CBW */ + break; + case 0x99: /* CDQ/CWD */ + break; + case 0x1af: /* imul Gv, Ev */ + case 0x69: /* imul Gv, Ev, I */ + case 0x6b: + ot = dflag ? OT_LONG : OT_WORD; + modrm = ldub_code(s->pc++); + parse_modrm(s, modrm); + if (b == 0x69) { + insn_get(s, ot); + } else if (b == 0x6b) { + insn_get(s, OT_BYTE); + } else { + } + break; + + case 0x84: /* test Ev, Gv */ + case 0x85: + + case 0x1c0: + case 0x1c1: /* xadd Ev, Gv */ + + case 0x1b0: + case 0x1b1: /* cmpxchg Ev, Gv */ + + case 0x8f: /* pop Ev */ + + case 0x88: + case 0x89: /* mov Gv, Ev */ + + case 0x8a: + case 0x8b: /* mov Ev, Gv */ + + case 0x1b6: /* movzbS Gv, Eb */ + case 0x1b7: /* movzwS Gv, Eb */ + case 0x1be: /* movsbS Gv, Eb */ + case 0x1bf: /* movswS Gv, Eb */ + + case 0x86: + case 0x87: /* xchg Ev, Gv */ + + case 0xd0: + case 0xd1: /* shift Ev,1 */ + + case 0xd2: + case 0xd3: /* shift Ev,cl */ + + case 0x1a5: /* shld cl */ + case 0x1ad: /* shrd cl */ + + case 0x190 ... 0x19f: /* setcc Gv */ + + /* XXX: emulate cmov if not available ? */ + case 0x140 ... 0x14f: /* cmov Gv, Ev */ + + case 0x1a3: /* bt Gv, Ev */ + case 0x1ab: /* bts */ + case 0x1b3: /* btr */ + case 0x1bb: /* btc */ + + case 0x1bc: /* bsf */ + case 0x1bd: /* bsr */ + + modrm = ldub_code(s->pc++); + parse_modrm(s, modrm); + break; + + case 0x1c7: /* cmpxchg8b */ + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + if (mod == 3) + goto illegal_op; + parse_modrm(s, modrm); + break; + + /**************************/ + /* push/pop */ + case 0x50 ... 0x57: /* push */ + case 0x58 ... 0x5f: /* pop */ + case 0x60: /* pusha */ + case 0x61: /* popa */ + break; + + case 0x68: /* push Iv */ + case 0x6a: + ot = dflag ? OT_LONG : OT_WORD; + if (b == 0x68) + insn_get(s, ot); + else + insn_get(s, OT_BYTE); + break; + case 0xc8: /* enter */ + lduw_code(s->pc); + s->pc += 2; + ldub_code(s->pc++); + break; + case 0xc9: /* leave */ + break; + + case 0x06: /* push es */ + case 0x0e: /* push cs */ + case 0x16: /* push ss */ + case 0x1e: /* push ds */ + /* XXX: optimize: + push segs[n].selector + */ + goto unsupported_op; + case 0x1a0: /* push fs */ + case 0x1a8: /* push gs */ + goto unsupported_op; + case 0x07: /* pop es */ + case 0x17: /* pop ss */ + case 0x1f: /* pop ds */ + goto unsupported_op; + case 0x1a1: /* pop fs */ + case 0x1a9: /* pop gs */ + goto unsupported_op; + case 0x8e: /* mov seg, Gv */ + /* XXX: optimize: + fs movl r, regs[] + movl segs[].selector, r + mov r, Gv + fs movl regs[], r + */ + goto unsupported_op; + case 0x8c: /* mov Gv, seg */ + goto unsupported_op; + case 0xc4: /* les Gv */ + op = R_ES; + goto do_lxx; + case 0xc5: /* lds Gv */ + op = R_DS; + goto do_lxx; + case 0x1b2: /* lss Gv */ + op = R_SS; + goto do_lxx; + case 0x1b4: /* lfs Gv */ + op = R_FS; + goto do_lxx; + case 0x1b5: /* lgs Gv */ + op = R_GS; + do_lxx: + goto unsupported_op; + /************************/ + /* floats */ + case 0xd8 ... 0xdf: +#if 1 + /* currently not stable enough */ + goto unsupported_op; +#else + if (s->flags & (HF_EM_MASK | HF_TS_MASK)) + goto unsupported_op; +#endif +#if 0 + /* for testing FPU context switch */ + { + static int count; + count = (count + 1) % 3; + if (count != 0) + goto unsupported_op; + } +#endif + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + rm = modrm & 7; + op = ((b & 7) << 3) | ((modrm >> 3) & 7); + if (mod != 3) { + /* memory op */ + parse_modrm(s, modrm); + switch(op) { + case 0x00 ... 0x07: /* fxxxs */ + case 0x10 ... 0x17: /* fixxxl */ + case 0x20 ... 0x27: /* fxxxl */ + case 0x30 ... 0x37: /* fixxx */ + break; + case 0x08: /* flds */ + case 0x0a: /* fsts */ + case 0x0b: /* fstps */ + case 0x18: /* fildl */ + case 0x1a: /* fistl */ + case 0x1b: /* fistpl */ + case 0x28: /* fldl */ + case 0x2a: /* fstl */ + case 0x2b: /* fstpl */ + case 0x38: /* filds */ + case 0x3a: /* fists */ + case 0x3b: /* fistps */ + case 0x0c: /* fldenv mem */ + case 0x0d: /* fldcw mem */ + case 0x0e: /* fnstenv mem */ + case 0x0f: /* fnstcw mem */ + case 0x1d: /* fldt mem */ + case 0x1f: /* fstpt mem */ + case 0x2c: /* frstor mem */ + case 0x2e: /* fnsave mem */ + case 0x2f: /* fnstsw mem */ + case 0x3c: /* fbld */ + case 0x3e: /* fbstp */ + case 0x3d: /* fildll */ + case 0x3f: /* fistpll */ + break; + default: + goto illegal_op; + } + } else { + /* register float ops */ + switch(op) { + case 0x08: /* fld sti */ + case 0x09: /* fxchg sti */ + break; + case 0x0a: /* grp d9/2 */ + switch(rm) { + case 0: /* fnop */ + break; + default: + goto illegal_op; + } + break; + case 0x0c: /* grp d9/4 */ + switch(rm) { + case 0: /* fchs */ + case 1: /* fabs */ + case 4: /* ftst */ + case 5: /* fxam */ + break; + default: + goto illegal_op; + } + break; + case 0x0d: /* grp d9/5 */ + switch(rm) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + break; + default: + goto illegal_op; + } + break; + case 0x0e: /* grp d9/6 */ + break; + case 0x0f: /* grp d9/7 */ + break; + case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */ + case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */ + case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */ + break; + case 0x02: /* fcom */ + break; + case 0x03: /* fcomp */ + break; + case 0x15: /* da/5 */ + switch(rm) { + case 1: /* fucompp */ + break; + default: + goto illegal_op; + } + break; + case 0x1c: + switch(rm) { + case 0: /* feni (287 only, just do nop here) */ + case 1: /* fdisi (287 only, just do nop here) */ + goto unsupported_op; + case 2: /* fclex */ + case 3: /* fninit */ + case 4: /* fsetpm (287 only, just do nop here) */ + break; + default: + goto illegal_op; + } + break; + case 0x1d: /* fucomi */ + break; + case 0x1e: /* fcomi */ + break; + case 0x28: /* ffree sti */ + break; + case 0x2a: /* fst sti */ + break; + case 0x2b: /* fstp sti */ + break; + case 0x2c: /* fucom st(i) */ + break; + case 0x2d: /* fucomp st(i) */ + break; + case 0x33: /* de/3 */ + switch(rm) { + case 1: /* fcompp */ + break; + default: + goto illegal_op; + } + break; + case 0x3c: /* df/4 */ + switch(rm) { + case 0: + break; + default: + goto illegal_op; + } + break; + case 0x3d: /* fucomip */ + break; + case 0x3e: /* fcomip */ + break; + case 0x10 ... 0x13: /* fcmovxx */ + case 0x18 ... 0x1b: + break; + default: + goto illegal_op; + } + } + s->tb->cflags |= CF_TB_FP_USED; + break; + + /**************************/ + /* mov */ + case 0xc6: + case 0xc7: /* mov Ev, Iv */ + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + modrm = ldub_code(s->pc++); + parse_modrm(s, modrm); + insn_get(s, ot); + break; + + case 0x8d: /* lea */ + ot = dflag ? OT_LONG : OT_WORD; + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + if (mod == 3) + goto illegal_op; + parse_modrm(s, modrm); + break; + + case 0xa0: /* mov EAX, Ov */ + case 0xa1: + case 0xa2: /* mov Ov, EAX */ + case 0xa3: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + if (s->aflag) + insn_get(s, OT_LONG); + else + insn_get(s, OT_WORD); + break; + case 0xd7: /* xlat */ + break; + case 0xb0 ... 0xb7: /* mov R, Ib */ + insn_get(s, OT_BYTE); + break; + case 0xb8 ... 0xbf: /* mov R, Iv */ + ot = dflag ? OT_LONG : OT_WORD; + insn_get(s, ot); + break; + + case 0x91 ... 0x97: /* xchg R, EAX */ + break; + + /************************/ + /* shifts */ + case 0xc0: + case 0xc1: /* shift Ev,imm */ + + case 0x1a4: /* shld imm */ + case 0x1ac: /* shrd imm */ + modrm = ldub_code(s->pc++); + parse_modrm(s, modrm); + ldub_code(s->pc++); + break; + + /************************/ + /* string ops */ + + case 0xa4: /* movsS */ + case 0xa5: + break; + + case 0xaa: /* stosS */ + case 0xab: + break; + + case 0xac: /* lodsS */ + case 0xad: + break; + + case 0xae: /* scasS */ + case 0xaf: + break; + + case 0xa6: /* cmpsS */ + case 0xa7: + break; + + case 0x6c: /* insS */ + case 0x6d: + goto unsupported_op; + + case 0x6e: /* outsS */ + case 0x6f: + goto unsupported_op; + + /************************/ + /* port I/O */ + case 0xe4: + case 0xe5: + goto unsupported_op; + + case 0xe6: + case 0xe7: + goto unsupported_op; + + case 0xec: + case 0xed: + goto unsupported_op; + + case 0xee: + case 0xef: + goto unsupported_op; + + /************************/ + /* control */ +#if 0 + case 0xc2: /* ret im */ + val = ldsw_code(s->pc); + s->pc += 2; + gen_pop_T0(s); + gen_stack_update(s, val + (2 << s->dflag)); + if (s->dflag == 0) + gen_op_andl_T0_ffff(); + gen_op_jmp_T0(); + gen_eob(s); + break; +#endif + + case 0xc3: /* ret */ + gb(s, CPU_SEG); + if (!s->dflag) + gb(s, 0x66); /* d16 */ + gb(s, 0x8f); /* pop addr */ + gb(s, 0x05); + gl(s, CPU_FIELD_OFFSET(eip)); + if (!s->dflag) { + /* reset high bits of EIP */ + gen_movw_addr_im(s, CPU_FIELD_OFFSET(eip) + 2, 0); + } + gen_eob(s); + goto no_copy; + case 0xca: /* lret im */ + case 0xcb: /* lret */ + case 0xcf: /* iret */ + case 0x9a: /* lcall im */ + case 0xea: /* ljmp im */ + goto unsupported_op; + + case 0xe8: /* call im */ + ot = dflag ? OT_LONG : OT_WORD; + val = insn_get(s, ot); + next_eip = s->pc - s->cs_base; + val += next_eip; + if (s->dflag) { + gb(s, 0x68); /* pushl imm */ + gl(s, next_eip); + } else { + gb(s, 0x66); /* pushw imm */ + gb(s, 0x68); + gw(s, next_eip); + val &= 0xffff; + } + gen_jmp(s, val); + goto no_copy; + case 0xe9: /* jmp */ + ot = dflag ? OT_LONG : OT_WORD; + val = insn_get(s, ot); + val += s->pc - s->cs_base; + if (s->dflag == 0) + val = val & 0xffff; + gen_jmp(s, val); + goto no_copy; + case 0xeb: /* jmp Jb */ + val = (int8_t)insn_get(s, OT_BYTE); + val += s->pc - s->cs_base; + if (s->dflag == 0) + val = val & 0xffff; + gen_jmp(s, val); + goto no_copy; + case 0x70 ... 0x7f: /* jcc Jb */ + val = (int8_t)insn_get(s, OT_BYTE); + goto do_jcc; + case 0x180 ... 0x18f: /* jcc Jv */ + if (dflag) { + val = insn_get(s, OT_LONG); + } else { + val = (int16_t)insn_get(s, OT_WORD); + } + do_jcc: + next_eip = s->pc - s->cs_base; + val += next_eip; + if (s->dflag == 0) + val &= 0xffff; + gen_jcc(s, b & 0xf, val, next_eip); + goto no_copy; + + /************************/ + /* flags */ + case 0x9c: /* pushf */ + /* XXX: put specific code ? */ + goto unsupported_op; + case 0x9d: /* popf */ + goto unsupported_op; + + case 0x9e: /* sahf */ + case 0x9f: /* lahf */ + case 0xf5: /* cmc */ + case 0xf8: /* clc */ + case 0xf9: /* stc */ + case 0xfc: /* cld */ + case 0xfd: /* std */ + break; + + /************************/ + /* bit operations */ + case 0x1ba: /* bt/bts/btr/btc Gv, im */ + ot = dflag ? OT_LONG : OT_WORD; + modrm = ldub_code(s->pc++); + op = (modrm >> 3) & 7; + parse_modrm(s, modrm); + /* load shift */ + ldub_code(s->pc++); + if (op < 4) + goto illegal_op; + break; + /************************/ + /* bcd */ + case 0x27: /* daa */ + break; + case 0x2f: /* das */ + break; + case 0x37: /* aaa */ + break; + case 0x3f: /* aas */ + break; + case 0xd4: /* aam */ + ldub_code(s->pc++); + break; + case 0xd5: /* aad */ + ldub_code(s->pc++); + break; + /************************/ + /* misc */ + case 0x90: /* nop */ + break; + case 0x9b: /* fwait */ + if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) == + (HF_MP_MASK | HF_TS_MASK)) { + goto unsupported_op; + } + break; + case 0xcc: /* int3 */ + goto unsupported_op; + case 0xcd: /* int N */ + goto unsupported_op; + case 0xce: /* into */ + goto unsupported_op; + case 0xf1: /* icebp (undocumented, exits to external debugger) */ + goto unsupported_op; + case 0xfa: /* cli */ + goto unsupported_op; + case 0xfb: /* sti */ + goto unsupported_op; + case 0x62: /* bound */ + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + if (mod == 3) + goto illegal_op; + parse_modrm(s, modrm); + break; + case 0x1c8 ... 0x1cf: /* bswap reg */ + break; + case 0xd6: /* salc */ + break; + case 0xe0: /* loopnz */ + case 0xe1: /* loopz */ + case 0xe2: /* loop */ + case 0xe3: /* jecxz */ + goto unsupported_op; + + case 0x130: /* wrmsr */ + case 0x132: /* rdmsr */ + goto unsupported_op; + case 0x131: /* rdtsc */ + goto unsupported_op; + case 0x1a2: /* cpuid */ + goto unsupported_op; + case 0xf4: /* hlt */ + goto unsupported_op; + case 0x100: + goto unsupported_op; + case 0x101: + goto unsupported_op; + case 0x108: /* invd */ + case 0x109: /* wbinvd */ + goto unsupported_op; + case 0x63: /* arpl */ + goto unsupported_op; + case 0x102: /* lar */ + case 0x103: /* lsl */ + goto unsupported_op; + case 0x118: + goto unsupported_op; + case 0x120: /* mov reg, crN */ + case 0x122: /* mov crN, reg */ + goto unsupported_op; + case 0x121: /* mov reg, drN */ + case 0x123: /* mov drN, reg */ + goto unsupported_op; + case 0x106: /* clts */ + goto unsupported_op; + default: + goto illegal_op; + } + + /* just copy the code */ + + /* no override yet */ + if (!s->dflag) + gb(s, 0x66); + if (!s->aflag) + gb(s, 0x67); + if (prefixes & PREFIX_REPZ) + gb(s, 0xf3); + else if (prefixes & PREFIX_REPNZ) + gb(s, 0xf2); + { + int len, i; + len = s->pc - pc_start_insn; + for(i = 0; i < len; i++) { + *s->gen_code_ptr++ = ldub_code(pc_start_insn + i); + } + } + no_copy: + return 0; + illegal_op: + unsupported_op: + /* fall back to slower code gen necessary */ + s->pc = pc_start; + return -1; +} + +#define GEN_CODE_MAX_SIZE 8192 +#define GEN_CODE_MAX_INSN_SIZE 512 + +static inline int gen_intermediate_code_internal(CPUState *env, + TranslationBlock *tb, + uint8_t *gen_code_ptr, + int *gen_code_size_ptr, + int search_pc, + uint8_t *tc_ptr) +{ + DisasContext dc1, *dc = &dc1; + target_ulong pc_insn, pc_start, cs_base; + uint8_t *gen_code_end; + int flags, ret; + + if (env->nb_breakpoints > 0 || + env->singlestep_enabled) + return -1; + flags = tb->flags; + if (flags & (HF_TF_MASK | HF_ADDSEG_MASK | + HF_SOFTMMU_MASK | HF_INHIBIT_IRQ_MASK)) + return -1; + if (!(flags & HF_SS32_MASK)) + return -1; + if (tb->cflags & CF_SINGLE_INSN) + return -1; + gen_code_end = gen_code_ptr + + GEN_CODE_MAX_SIZE - GEN_CODE_MAX_INSN_SIZE; + dc->gen_code_ptr = gen_code_ptr; + dc->gen_code_start = gen_code_ptr; + + /* generate intermediate code */ + pc_start = tb->pc; + cs_base = tb->cs_base; + dc->pc = pc_start; + dc->cs_base = cs_base; + dc->pe = (flags >> HF_PE_SHIFT) & 1; + dc->code32 = (flags >> HF_CS32_SHIFT) & 1; + dc->f_st = 0; + dc->vm86 = (flags >> VM_SHIFT) & 1; + dc->cpl = (flags >> HF_CPL_SHIFT) & 3; + dc->iopl = (flags >> IOPL_SHIFT) & 3; + dc->tb = tb; + dc->flags = flags; + + dc->is_jmp = 0; + + for(;;) { + pc_insn = dc->pc; + ret = disas_insn(dc); + if (ret < 0) { + /* unsupported insn */ + if (dc->pc == pc_start) { + /* if first instruction, signal that no copying was done */ + return -1; + } else { + gen_jmp(dc, dc->pc - dc->cs_base); + dc->is_jmp = 1; + } + } + if (search_pc) { + /* search pc mode */ + if (tc_ptr < dc->gen_code_ptr) { + env->eip = pc_insn - cs_base; + return 0; + } + } + /* stop translation if indicated */ + if (dc->is_jmp) + break; + /* if too long translation, stop generation */ + if (dc->gen_code_ptr >= gen_code_end || + (dc->pc - pc_start) >= (TARGET_PAGE_SIZE - 32)) { + gen_jmp(dc, dc->pc - dc->cs_base); + break; + } + } + +#ifdef DEBUG_DISAS + if (loglevel & CPU_LOG_TB_IN_ASM) { + fprintf(logfile, "----------------\n"); + fprintf(logfile, "IN: COPY: %s fpu=%d\n", + lookup_symbol(pc_start), + tb->cflags & CF_TB_FP_USED ? 1 : 0); + target_disas(logfile, pc_start, dc->pc - pc_start, !dc->code32); + fprintf(logfile, "\n"); + } +#endif + + if (!search_pc) { + *gen_code_size_ptr = dc->gen_code_ptr - dc->gen_code_start; + tb->size = dc->pc - pc_start; + tb->cflags |= CF_CODE_COPY; + return 0; + } else { + return -1; + } +} + +/* generate code by just copying data. Return -1 if cannot generate + any code. Return 0 if code was generated */ +int cpu_gen_code_copy(CPUState *env, TranslationBlock *tb, + int max_code_size, int *gen_code_size_ptr) +{ + /* generate machine code */ + tb->tb_next_offset[0] = 0xffff; + tb->tb_next_offset[1] = 0xffff; +#ifdef USE_DIRECT_JUMP + /* the following two entries are optional (only used for string ops) */ + tb->tb_jmp_offset[2] = 0xffff; + tb->tb_jmp_offset[3] = 0xffff; +#endif + return gen_intermediate_code_internal(env, tb, + tb->tc_ptr, gen_code_size_ptr, + 0, NULL); +} + +static uint8_t dummy_gen_code_buf[GEN_CODE_MAX_SIZE]; + +int cpu_restore_state_copy(TranslationBlock *tb, + CPUState *env, unsigned long searched_pc, + void *puc) +{ + struct ucontext *uc = puc; + int ret, eflags; + + /* find opc index corresponding to search_pc */ + if (searched_pc < (unsigned long)tb->tc_ptr) + return -1; + searched_pc = searched_pc - (long)tb->tc_ptr + (long)dummy_gen_code_buf; + ret = gen_intermediate_code_internal(env, tb, + dummy_gen_code_buf, NULL, + 1, (uint8_t *)searched_pc); + if (ret < 0) + return ret; + /* restore all the CPU state from the CPU context from the + signal. The FPU context stays in the host CPU. */ + + env->regs[R_EAX] = uc->uc_mcontext.gregs[REG_EAX]; + env->regs[R_ECX] = uc->uc_mcontext.gregs[REG_ECX]; + env->regs[R_EDX] = uc->uc_mcontext.gregs[REG_EDX]; + env->regs[R_EBX] = uc->uc_mcontext.gregs[REG_EBX]; + env->regs[R_ESP] = uc->uc_mcontext.gregs[REG_ESP]; + env->regs[R_EBP] = uc->uc_mcontext.gregs[REG_EBP]; + env->regs[R_ESI] = uc->uc_mcontext.gregs[REG_ESI]; + env->regs[R_EDI] = uc->uc_mcontext.gregs[REG_EDI]; + eflags = uc->uc_mcontext.gregs[REG_EFL]; + env->df = 1 - (2 * ((eflags >> 10) & 1)); + env->cc_src = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); + env->cc_op = CC_OP_EFLAGS; + return 0; +} + +#endif /* USE_CODE_COPY */ diff --git a/tools/ioemu/target-i386/translate.c b/tools/ioemu/target-i386/translate.c new file mode 100644 index 0000000000..a1b91d3539 --- /dev/null +++ b/tools/ioemu/target-i386/translate.c @@ -0,0 +1,6468 @@ +/* + * i386 translation + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include + +#include "cpu.h" +#include "exec-all.h" +#include "disas.h" + +/* XXX: move that elsewhere */ +static uint16_t *gen_opc_ptr; +static uint32_t *gen_opparam_ptr; + +#define PREFIX_REPZ 0x01 +#define PREFIX_REPNZ 0x02 +#define PREFIX_LOCK 0x04 +#define PREFIX_DATA 0x08 +#define PREFIX_ADR 0x10 + +#ifdef TARGET_X86_64 +#define X86_64_ONLY(x) x +#define X86_64_DEF(x...) x +#define CODE64(s) ((s)->code64) +#define REX_X(s) ((s)->rex_x) +#define REX_B(s) ((s)->rex_b) +/* XXX: gcc generates push/pop in some opcodes, so we cannot use them */ +#if 1 +#define BUGGY_64(x) NULL +#endif +#else +#define X86_64_ONLY(x) NULL +#define X86_64_DEF(x...) +#define CODE64(s) 0 +#define REX_X(s) 0 +#define REX_B(s) 0 +#endif + +#ifdef TARGET_X86_64 +static int x86_64_hregs; +#endif + +#ifdef USE_DIRECT_JUMP +#define TBPARAM(x) +#else +#define TBPARAM(x) (long)(x) +#endif + +typedef struct DisasContext { + /* current insn context */ + int override; /* -1 if no override */ + int prefix; + int aflag, dflag; + target_ulong pc; /* pc = eip + cs_base */ + int is_jmp; /* 1 = means jump (stop translation), 2 means CPU + static state change (stop translation) */ + /* current block context */ + target_ulong cs_base; /* base of CS segment */ + int pe; /* protected mode */ + int code32; /* 32 bit code segment */ +#ifdef TARGET_X86_64 + int lma; /* long mode active */ + int code64; /* 64 bit code segment */ + int rex_x, rex_b; +#endif + int ss32; /* 32 bit stack segment */ + int cc_op; /* current CC operation */ + int addseg; /* non zero if either DS/ES/SS have a non zero base */ + int f_st; /* currently unused */ + int vm86; /* vm86 mode */ + int cpl; + int iopl; + int tf; /* TF cpu flag */ + int singlestep_enabled; /* "hardware" single step enabled */ + int jmp_opt; /* use direct block chaining for direct jumps */ + int mem_index; /* select memory access functions */ + int flags; /* all execution flags */ + struct TranslationBlock *tb; + int popl_esp_hack; /* for correct popl with esp base handling */ + int rip_offset; /* only used in x86_64, but left for simplicity */ + int cpuid_features; +} DisasContext; + +static void gen_eob(DisasContext *s); +static void gen_jmp(DisasContext *s, target_ulong eip); +static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num); + +/* i386 arith/logic operations */ +enum { + OP_ADDL, + OP_ORL, + OP_ADCL, + OP_SBBL, + OP_ANDL, + OP_SUBL, + OP_XORL, + OP_CMPL, +}; + +/* i386 shift ops */ +enum { + OP_ROL, + OP_ROR, + OP_RCL, + OP_RCR, + OP_SHL, + OP_SHR, + OP_SHL1, /* undocumented */ + OP_SAR = 7, +}; + +enum { +#define DEF(s, n, copy_size) INDEX_op_ ## s, +#include "opc.h" +#undef DEF + NB_OPS, +}; + +#include "gen-op.h" + +/* operand size */ +enum { + OT_BYTE = 0, + OT_WORD, + OT_LONG, + OT_QUAD, +}; + +enum { + /* I386 int registers */ + OR_EAX, /* MUST be even numbered */ + OR_ECX, + OR_EDX, + OR_EBX, + OR_ESP, + OR_EBP, + OR_ESI, + OR_EDI, + + OR_TMP0 = 16, /* temporary operand register */ + OR_TMP1, + OR_A0, /* temporary register used when doing address evaluation */ +}; + +#ifdef TARGET_X86_64 + +#define NB_OP_SIZES 4 + +#define DEF_REGS(prefix, suffix) \ + prefix ## EAX ## suffix,\ + prefix ## ECX ## suffix,\ + prefix ## EDX ## suffix,\ + prefix ## EBX ## suffix,\ + prefix ## ESP ## suffix,\ + prefix ## EBP ## suffix,\ + prefix ## ESI ## suffix,\ + prefix ## EDI ## suffix,\ + prefix ## R8 ## suffix,\ + prefix ## R9 ## suffix,\ + prefix ## R10 ## suffix,\ + prefix ## R11 ## suffix,\ + prefix ## R12 ## suffix,\ + prefix ## R13 ## suffix,\ + prefix ## R14 ## suffix,\ + prefix ## R15 ## suffix, + +#define DEF_BREGS(prefixb, prefixh, suffix) \ + \ +static void prefixb ## ESP ## suffix ## _wrapper(void) \ +{ \ + if (x86_64_hregs) \ + prefixb ## ESP ## suffix (); \ + else \ + prefixh ## EAX ## suffix (); \ +} \ + \ +static void prefixb ## EBP ## suffix ## _wrapper(void) \ +{ \ + if (x86_64_hregs) \ + prefixb ## EBP ## suffix (); \ + else \ + prefixh ## ECX ## suffix (); \ +} \ + \ +static void prefixb ## ESI ## suffix ## _wrapper(void) \ +{ \ + if (x86_64_hregs) \ + prefixb ## ESI ## suffix (); \ + else \ + prefixh ## EDX ## suffix (); \ +} \ + \ +static void prefixb ## EDI ## suffix ## _wrapper(void) \ +{ \ + if (x86_64_hregs) \ + prefixb ## EDI ## suffix (); \ + else \ + prefixh ## EBX ## suffix (); \ +} + +DEF_BREGS(gen_op_movb_, gen_op_movh_, _T0) +DEF_BREGS(gen_op_movb_, gen_op_movh_, _T1) +DEF_BREGS(gen_op_movl_T0_, gen_op_movh_T0_, ) +DEF_BREGS(gen_op_movl_T1_, gen_op_movh_T1_, ) + +#else /* !TARGET_X86_64 */ + +#define NB_OP_SIZES 3 + +#define DEF_REGS(prefix, suffix) \ + prefix ## EAX ## suffix,\ + prefix ## ECX ## suffix,\ + prefix ## EDX ## suffix,\ + prefix ## EBX ## suffix,\ + prefix ## ESP ## suffix,\ + prefix ## EBP ## suffix,\ + prefix ## ESI ## suffix,\ + prefix ## EDI ## suffix, + +#endif /* !TARGET_X86_64 */ + +static GenOpFunc *gen_op_mov_reg_T0[NB_OP_SIZES][CPU_NB_REGS] = { + [OT_BYTE] = { + gen_op_movb_EAX_T0, + gen_op_movb_ECX_T0, + gen_op_movb_EDX_T0, + gen_op_movb_EBX_T0, +#ifdef TARGET_X86_64 + gen_op_movb_ESP_T0_wrapper, + gen_op_movb_EBP_T0_wrapper, + gen_op_movb_ESI_T0_wrapper, + gen_op_movb_EDI_T0_wrapper, + gen_op_movb_R8_T0, + gen_op_movb_R9_T0, + gen_op_movb_R10_T0, + gen_op_movb_R11_T0, + gen_op_movb_R12_T0, + gen_op_movb_R13_T0, + gen_op_movb_R14_T0, + gen_op_movb_R15_T0, +#else + gen_op_movh_EAX_T0, + gen_op_movh_ECX_T0, + gen_op_movh_EDX_T0, + gen_op_movh_EBX_T0, +#endif + }, + [OT_WORD] = { + DEF_REGS(gen_op_movw_, _T0) + }, + [OT_LONG] = { + DEF_REGS(gen_op_movl_, _T0) + }, +#ifdef TARGET_X86_64 + [OT_QUAD] = { + DEF_REGS(gen_op_movq_, _T0) + }, +#endif +}; + +static GenOpFunc *gen_op_mov_reg_T1[NB_OP_SIZES][CPU_NB_REGS] = { + [OT_BYTE] = { + gen_op_movb_EAX_T1, + gen_op_movb_ECX_T1, + gen_op_movb_EDX_T1, + gen_op_movb_EBX_T1, +#ifdef TARGET_X86_64 + gen_op_movb_ESP_T1_wrapper, + gen_op_movb_EBP_T1_wrapper, + gen_op_movb_ESI_T1_wrapper, + gen_op_movb_EDI_T1_wrapper, + gen_op_movb_R8_T1, + gen_op_movb_R9_T1, + gen_op_movb_R10_T1, + gen_op_movb_R11_T1, + gen_op_movb_R12_T1, + gen_op_movb_R13_T1, + gen_op_movb_R14_T1, + gen_op_movb_R15_T1, +#else + gen_op_movh_EAX_T1, + gen_op_movh_ECX_T1, + gen_op_movh_EDX_T1, + gen_op_movh_EBX_T1, +#endif + }, + [OT_WORD] = { + DEF_REGS(gen_op_movw_, _T1) + }, + [OT_LONG] = { + DEF_REGS(gen_op_movl_, _T1) + }, +#ifdef TARGET_X86_64 + [OT_QUAD] = { + DEF_REGS(gen_op_movq_, _T1) + }, +#endif +}; + +static GenOpFunc *gen_op_mov_reg_A0[NB_OP_SIZES - 1][CPU_NB_REGS] = { + [0] = { + DEF_REGS(gen_op_movw_, _A0) + }, + [1] = { + DEF_REGS(gen_op_movl_, _A0) + }, +#ifdef TARGET_X86_64 + [2] = { + DEF_REGS(gen_op_movq_, _A0) + }, +#endif +}; + +static GenOpFunc *gen_op_mov_TN_reg[NB_OP_SIZES][2][CPU_NB_REGS] = +{ + [OT_BYTE] = { + { + gen_op_movl_T0_EAX, + gen_op_movl_T0_ECX, + gen_op_movl_T0_EDX, + gen_op_movl_T0_EBX, +#ifdef TARGET_X86_64 + gen_op_movl_T0_ESP_wrapper, + gen_op_movl_T0_EBP_wrapper, + gen_op_movl_T0_ESI_wrapper, + gen_op_movl_T0_EDI_wrapper, + gen_op_movl_T0_R8, + gen_op_movl_T0_R9, + gen_op_movl_T0_R10, + gen_op_movl_T0_R11, + gen_op_movl_T0_R12, + gen_op_movl_T0_R13, + gen_op_movl_T0_R14, + gen_op_movl_T0_R15, +#else + gen_op_movh_T0_EAX, + gen_op_movh_T0_ECX, + gen_op_movh_T0_EDX, + gen_op_movh_T0_EBX, +#endif + }, + { + gen_op_movl_T1_EAX, + gen_op_movl_T1_ECX, + gen_op_movl_T1_EDX, + gen_op_movl_T1_EBX, +#ifdef TARGET_X86_64 + gen_op_movl_T1_ESP_wrapper, + gen_op_movl_T1_EBP_wrapper, + gen_op_movl_T1_ESI_wrapper, + gen_op_movl_T1_EDI_wrapper, + gen_op_movl_T1_R8, + gen_op_movl_T1_R9, + gen_op_movl_T1_R10, + gen_op_movl_T1_R11, + gen_op_movl_T1_R12, + gen_op_movl_T1_R13, + gen_op_movl_T1_R14, + gen_op_movl_T1_R15, +#else + gen_op_movh_T1_EAX, + gen_op_movh_T1_ECX, + gen_op_movh_T1_EDX, + gen_op_movh_T1_EBX, +#endif + }, + }, + [OT_WORD] = { + { + DEF_REGS(gen_op_movl_T0_, ) + }, + { + DEF_REGS(gen_op_movl_T1_, ) + }, + }, + [OT_LONG] = { + { + DEF_REGS(gen_op_movl_T0_, ) + }, + { + DEF_REGS(gen_op_movl_T1_, ) + }, + }, +#ifdef TARGET_X86_64 + [OT_QUAD] = { + { + DEF_REGS(gen_op_movl_T0_, ) + }, + { + DEF_REGS(gen_op_movl_T1_, ) + }, + }, +#endif +}; + +static GenOpFunc *gen_op_movl_A0_reg[CPU_NB_REGS] = { + DEF_REGS(gen_op_movl_A0_, ) +}; + +static GenOpFunc *gen_op_addl_A0_reg_sN[4][CPU_NB_REGS] = { + [0] = { + DEF_REGS(gen_op_addl_A0_, ) + }, + [1] = { + DEF_REGS(gen_op_addl_A0_, _s1) + }, + [2] = { + DEF_REGS(gen_op_addl_A0_, _s2) + }, + [3] = { + DEF_REGS(gen_op_addl_A0_, _s3) + }, +}; + +#ifdef TARGET_X86_64 +static GenOpFunc *gen_op_movq_A0_reg[CPU_NB_REGS] = { + DEF_REGS(gen_op_movq_A0_, ) +}; + +static GenOpFunc *gen_op_addq_A0_reg_sN[4][CPU_NB_REGS] = { + [0] = { + DEF_REGS(gen_op_addq_A0_, ) + }, + [1] = { + DEF_REGS(gen_op_addq_A0_, _s1) + }, + [2] = { + DEF_REGS(gen_op_addq_A0_, _s2) + }, + [3] = { + DEF_REGS(gen_op_addq_A0_, _s3) + }, +}; +#endif + +static GenOpFunc *gen_op_cmov_reg_T1_T0[NB_OP_SIZES - 1][CPU_NB_REGS] = { + [0] = { + DEF_REGS(gen_op_cmovw_, _T1_T0) + }, + [1] = { + DEF_REGS(gen_op_cmovl_, _T1_T0) + }, +#ifdef TARGET_X86_64 + [2] = { + DEF_REGS(gen_op_cmovq_, _T1_T0) + }, +#endif +}; + +static GenOpFunc *gen_op_arith_T0_T1_cc[8] = { + NULL, + gen_op_orl_T0_T1, + NULL, + NULL, + gen_op_andl_T0_T1, + NULL, + gen_op_xorl_T0_T1, + NULL, +}; + +#define DEF_ARITHC(SUFFIX)\ + {\ + gen_op_adcb ## SUFFIX ## _T0_T1_cc,\ + gen_op_sbbb ## SUFFIX ## _T0_T1_cc,\ + },\ + {\ + gen_op_adcw ## SUFFIX ## _T0_T1_cc,\ + gen_op_sbbw ## SUFFIX ## _T0_T1_cc,\ + },\ + {\ + gen_op_adcl ## SUFFIX ## _T0_T1_cc,\ + gen_op_sbbl ## SUFFIX ## _T0_T1_cc,\ + },\ + {\ + X86_64_ONLY(gen_op_adcq ## SUFFIX ## _T0_T1_cc),\ + X86_64_ONLY(gen_op_sbbq ## SUFFIX ## _T0_T1_cc),\ + }, + +static GenOpFunc *gen_op_arithc_T0_T1_cc[4][2] = { + DEF_ARITHC( ) +}; + +static GenOpFunc *gen_op_arithc_mem_T0_T1_cc[3 * 4][2] = { + DEF_ARITHC(_raw) +#ifndef CONFIG_USER_ONLY + DEF_ARITHC(_kernel) + DEF_ARITHC(_user) +#endif +}; + +static const int cc_op_arithb[8] = { + CC_OP_ADDB, + CC_OP_LOGICB, + CC_OP_ADDB, + CC_OP_SUBB, + CC_OP_LOGICB, + CC_OP_SUBB, + CC_OP_LOGICB, + CC_OP_SUBB, +}; + +#define DEF_CMPXCHG(SUFFIX)\ + gen_op_cmpxchgb ## SUFFIX ## _T0_T1_EAX_cc,\ + gen_op_cmpxchgw ## SUFFIX ## _T0_T1_EAX_cc,\ + gen_op_cmpxchgl ## SUFFIX ## _T0_T1_EAX_cc,\ + X86_64_ONLY(gen_op_cmpxchgq ## SUFFIX ## _T0_T1_EAX_cc), + +static GenOpFunc *gen_op_cmpxchg_T0_T1_EAX_cc[4] = { + DEF_CMPXCHG( ) +}; + +static GenOpFunc *gen_op_cmpxchg_mem_T0_T1_EAX_cc[3 * 4] = { + DEF_CMPXCHG(_raw) +#ifndef CONFIG_USER_ONLY + DEF_CMPXCHG(_kernel) + DEF_CMPXCHG(_user) +#endif +}; + +#define DEF_SHIFT(SUFFIX)\ + {\ + gen_op_rolb ## SUFFIX ## _T0_T1_cc,\ + gen_op_rorb ## SUFFIX ## _T0_T1_cc,\ + gen_op_rclb ## SUFFIX ## _T0_T1_cc,\ + gen_op_rcrb ## SUFFIX ## _T0_T1_cc,\ + gen_op_shlb ## SUFFIX ## _T0_T1_cc,\ + gen_op_shrb ## SUFFIX ## _T0_T1_cc,\ + gen_op_shlb ## SUFFIX ## _T0_T1_cc,\ + gen_op_sarb ## SUFFIX ## _T0_T1_cc,\ + },\ + {\ + gen_op_rolw ## SUFFIX ## _T0_T1_cc,\ + gen_op_rorw ## SUFFIX ## _T0_T1_cc,\ + gen_op_rclw ## SUFFIX ## _T0_T1_cc,\ + gen_op_rcrw ## SUFFIX ## _T0_T1_cc,\ + gen_op_shlw ## SUFFIX ## _T0_T1_cc,\ + gen_op_shrw ## SUFFIX ## _T0_T1_cc,\ + gen_op_shlw ## SUFFIX ## _T0_T1_cc,\ + gen_op_sarw ## SUFFIX ## _T0_T1_cc,\ + },\ + {\ + gen_op_roll ## SUFFIX ## _T0_T1_cc,\ + gen_op_rorl ## SUFFIX ## _T0_T1_cc,\ + gen_op_rcll ## SUFFIX ## _T0_T1_cc,\ + gen_op_rcrl ## SUFFIX ## _T0_T1_cc,\ + gen_op_shll ## SUFFIX ## _T0_T1_cc,\ + gen_op_shrl ## SUFFIX ## _T0_T1_cc,\ + gen_op_shll ## SUFFIX ## _T0_T1_cc,\ + gen_op_sarl ## SUFFIX ## _T0_T1_cc,\ + },\ + {\ + X86_64_ONLY(gen_op_rolq ## SUFFIX ## _T0_T1_cc),\ + X86_64_ONLY(gen_op_rorq ## SUFFIX ## _T0_T1_cc),\ + X86_64_ONLY(gen_op_rclq ## SUFFIX ## _T0_T1_cc),\ + X86_64_ONLY(gen_op_rcrq ## SUFFIX ## _T0_T1_cc),\ + X86_64_ONLY(gen_op_shlq ## SUFFIX ## _T0_T1_cc),\ + X86_64_ONLY(gen_op_shrq ## SUFFIX ## _T0_T1_cc),\ + X86_64_ONLY(gen_op_shlq ## SUFFIX ## _T0_T1_cc),\ + X86_64_ONLY(gen_op_sarq ## SUFFIX ## _T0_T1_cc),\ + }, + +static GenOpFunc *gen_op_shift_T0_T1_cc[4][8] = { + DEF_SHIFT( ) +}; + +static GenOpFunc *gen_op_shift_mem_T0_T1_cc[3 * 4][8] = { + DEF_SHIFT(_raw) +#ifndef CONFIG_USER_ONLY + DEF_SHIFT(_kernel) + DEF_SHIFT(_user) +#endif +}; + +#define DEF_SHIFTD(SUFFIX, op)\ + {\ + NULL,\ + NULL,\ + },\ + {\ + gen_op_shldw ## SUFFIX ## _T0_T1_ ## op ## _cc,\ + gen_op_shrdw ## SUFFIX ## _T0_T1_ ## op ## _cc,\ + },\ + {\ + gen_op_shldl ## SUFFIX ## _T0_T1_ ## op ## _cc,\ + gen_op_shrdl ## SUFFIX ## _T0_T1_ ## op ## _cc,\ + },\ + {\ +X86_64_DEF(gen_op_shldq ## SUFFIX ## _T0_T1_ ## op ## _cc,\ + gen_op_shrdq ## SUFFIX ## _T0_T1_ ## op ## _cc,)\ + }, + +static GenOpFunc1 *gen_op_shiftd_T0_T1_im_cc[4][2] = { + DEF_SHIFTD(, im) +}; + +static GenOpFunc *gen_op_shiftd_T0_T1_ECX_cc[4][2] = { + DEF_SHIFTD(, ECX) +}; + +static GenOpFunc1 *gen_op_shiftd_mem_T0_T1_im_cc[3 * 4][2] = { + DEF_SHIFTD(_raw, im) +#ifndef CONFIG_USER_ONLY + DEF_SHIFTD(_kernel, im) + DEF_SHIFTD(_user, im) +#endif +}; + +static GenOpFunc *gen_op_shiftd_mem_T0_T1_ECX_cc[3 * 4][2] = { + DEF_SHIFTD(_raw, ECX) +#ifndef CONFIG_USER_ONLY + DEF_SHIFTD(_kernel, ECX) + DEF_SHIFTD(_user, ECX) +#endif +}; + +static GenOpFunc *gen_op_btx_T0_T1_cc[3][4] = { + [0] = { + gen_op_btw_T0_T1_cc, + gen_op_btsw_T0_T1_cc, + gen_op_btrw_T0_T1_cc, + gen_op_btcw_T0_T1_cc, + }, + [1] = { + gen_op_btl_T0_T1_cc, + gen_op_btsl_T0_T1_cc, + gen_op_btrl_T0_T1_cc, + gen_op_btcl_T0_T1_cc, + }, +#ifdef TARGET_X86_64 + [2] = { + gen_op_btq_T0_T1_cc, + gen_op_btsq_T0_T1_cc, + gen_op_btrq_T0_T1_cc, + gen_op_btcq_T0_T1_cc, + }, +#endif +}; + +static GenOpFunc *gen_op_add_bit_A0_T1[3] = { + gen_op_add_bitw_A0_T1, + gen_op_add_bitl_A0_T1, + X86_64_ONLY(gen_op_add_bitq_A0_T1), +}; + +static GenOpFunc *gen_op_bsx_T0_cc[3][2] = { + [0] = { + gen_op_bsfw_T0_cc, + gen_op_bsrw_T0_cc, + }, + [1] = { + gen_op_bsfl_T0_cc, + gen_op_bsrl_T0_cc, + }, +#ifdef TARGET_X86_64 + [2] = { + gen_op_bsfq_T0_cc, + gen_op_bsrq_T0_cc, + }, +#endif +}; + +static GenOpFunc *gen_op_lds_T0_A0[3 * 4] = { + gen_op_ldsb_raw_T0_A0, + gen_op_ldsw_raw_T0_A0, + X86_64_ONLY(gen_op_ldsl_raw_T0_A0), + NULL, +#ifndef CONFIG_USER_ONLY + gen_op_ldsb_kernel_T0_A0, + gen_op_ldsw_kernel_T0_A0, + X86_64_ONLY(gen_op_ldsl_kernel_T0_A0), + NULL, + + gen_op_ldsb_user_T0_A0, + gen_op_ldsw_user_T0_A0, + X86_64_ONLY(gen_op_ldsl_user_T0_A0), + NULL, +#endif +}; + +static GenOpFunc *gen_op_ldu_T0_A0[3 * 4] = { + gen_op_ldub_raw_T0_A0, + gen_op_lduw_raw_T0_A0, + NULL, + NULL, + +#ifndef CONFIG_USER_ONLY + gen_op_ldub_kernel_T0_A0, + gen_op_lduw_kernel_T0_A0, + NULL, + NULL, + + gen_op_ldub_user_T0_A0, + gen_op_lduw_user_T0_A0, + NULL, + NULL, +#endif +}; + +/* sign does not matter, except for lidt/lgdt call (TODO: fix it) */ +static GenOpFunc *gen_op_ld_T0_A0[3 * 4] = { + gen_op_ldub_raw_T0_A0, + gen_op_lduw_raw_T0_A0, + gen_op_ldl_raw_T0_A0, + X86_64_ONLY(gen_op_ldq_raw_T0_A0), + +#ifndef CONFIG_USER_ONLY + gen_op_ldub_kernel_T0_A0, + gen_op_lduw_kernel_T0_A0, + gen_op_ldl_kernel_T0_A0, + X86_64_ONLY(gen_op_ldq_kernel_T0_A0), + + gen_op_ldub_user_T0_A0, + gen_op_lduw_user_T0_A0, + gen_op_ldl_user_T0_A0, + X86_64_ONLY(gen_op_ldq_user_T0_A0), +#endif +}; + +static GenOpFunc *gen_op_ld_T1_A0[3 * 4] = { + gen_op_ldub_raw_T1_A0, + gen_op_lduw_raw_T1_A0, + gen_op_ldl_raw_T1_A0, + X86_64_ONLY(gen_op_ldq_raw_T1_A0), + +#ifndef CONFIG_USER_ONLY + gen_op_ldub_kernel_T1_A0, + gen_op_lduw_kernel_T1_A0, + gen_op_ldl_kernel_T1_A0, + X86_64_ONLY(gen_op_ldq_kernel_T1_A0), + + gen_op_ldub_user_T1_A0, + gen_op_lduw_user_T1_A0, + gen_op_ldl_user_T1_A0, + X86_64_ONLY(gen_op_ldq_user_T1_A0), +#endif +}; + +static GenOpFunc *gen_op_st_T0_A0[3 * 4] = { + gen_op_stb_raw_T0_A0, + gen_op_stw_raw_T0_A0, + gen_op_stl_raw_T0_A0, + X86_64_ONLY(gen_op_stq_raw_T0_A0), + +#ifndef CONFIG_USER_ONLY + gen_op_stb_kernel_T0_A0, + gen_op_stw_kernel_T0_A0, + gen_op_stl_kernel_T0_A0, + X86_64_ONLY(gen_op_stq_kernel_T0_A0), + + gen_op_stb_user_T0_A0, + gen_op_stw_user_T0_A0, + gen_op_stl_user_T0_A0, + X86_64_ONLY(gen_op_stq_user_T0_A0), +#endif +}; + +static GenOpFunc *gen_op_st_T1_A0[3 * 4] = { + NULL, + gen_op_stw_raw_T1_A0, + gen_op_stl_raw_T1_A0, + X86_64_ONLY(gen_op_stq_raw_T1_A0), + +#ifndef CONFIG_USER_ONLY + NULL, + gen_op_stw_kernel_T1_A0, + gen_op_stl_kernel_T1_A0, + X86_64_ONLY(gen_op_stq_kernel_T1_A0), + + NULL, + gen_op_stw_user_T1_A0, + gen_op_stl_user_T1_A0, + X86_64_ONLY(gen_op_stq_user_T1_A0), +#endif +}; + +static inline void gen_jmp_im(target_ulong pc) +{ +#ifdef TARGET_X86_64 + if (pc == (uint32_t)pc) { + gen_op_movl_eip_im(pc); + } else if (pc == (int32_t)pc) { + gen_op_movq_eip_im(pc); + } else { + gen_op_movq_eip_im64(pc >> 32, pc); + } +#else + gen_op_movl_eip_im(pc); +#endif +} + +static inline void gen_string_movl_A0_ESI(DisasContext *s) +{ + int override; + + override = s->override; +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + if (override >= 0) { + gen_op_movq_A0_seg(offsetof(CPUX86State,segs[override].base)); + gen_op_addq_A0_reg_sN[0][R_ESI](); + } else { + gen_op_movq_A0_reg[R_ESI](); + } + } else +#endif + if (s->aflag) { + /* 32 bit address */ + if (s->addseg && override < 0) + override = R_DS; + if (override >= 0) { + gen_op_movl_A0_seg(offsetof(CPUX86State,segs[override].base)); + gen_op_addl_A0_reg_sN[0][R_ESI](); + } else { + gen_op_movl_A0_reg[R_ESI](); + } + } else { + /* 16 address, always override */ + if (override < 0) + override = R_DS; + gen_op_movl_A0_reg[R_ESI](); + gen_op_andl_A0_ffff(); + gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base)); + } +} + +static inline void gen_string_movl_A0_EDI(DisasContext *s) +{ +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + gen_op_movq_A0_reg[R_EDI](); + } else +#endif + if (s->aflag) { + if (s->addseg) { + gen_op_movl_A0_seg(offsetof(CPUX86State,segs[R_ES].base)); + gen_op_addl_A0_reg_sN[0][R_EDI](); + } else { + gen_op_movl_A0_reg[R_EDI](); + } + } else { + gen_op_movl_A0_reg[R_EDI](); + gen_op_andl_A0_ffff(); + gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_ES].base)); + } +} + +static GenOpFunc *gen_op_movl_T0_Dshift[4] = { + gen_op_movl_T0_Dshiftb, + gen_op_movl_T0_Dshiftw, + gen_op_movl_T0_Dshiftl, + X86_64_ONLY(gen_op_movl_T0_Dshiftq), +}; + +static GenOpFunc1 *gen_op_jnz_ecx[3] = { + gen_op_jnz_ecxw, + gen_op_jnz_ecxl, + X86_64_ONLY(gen_op_jnz_ecxq), +}; + +static GenOpFunc1 *gen_op_jz_ecx[3] = { + gen_op_jz_ecxw, + gen_op_jz_ecxl, + X86_64_ONLY(gen_op_jz_ecxq), +}; + +static GenOpFunc *gen_op_dec_ECX[3] = { + gen_op_decw_ECX, + gen_op_decl_ECX, + X86_64_ONLY(gen_op_decq_ECX), +}; + +static GenOpFunc1 *gen_op_string_jnz_sub[2][4] = { + { + gen_op_jnz_subb, + gen_op_jnz_subw, + gen_op_jnz_subl, + X86_64_ONLY(gen_op_jnz_subq), + }, + { + gen_op_jz_subb, + gen_op_jz_subw, + gen_op_jz_subl, + X86_64_ONLY(gen_op_jz_subq), + }, +}; + +static GenOpFunc *gen_op_in_DX_T0[3] = { + gen_op_inb_DX_T0, + gen_op_inw_DX_T0, + gen_op_inl_DX_T0, +}; + +static GenOpFunc *gen_op_out_DX_T0[3] = { + gen_op_outb_DX_T0, + gen_op_outw_DX_T0, + gen_op_outl_DX_T0, +}; + +static GenOpFunc *gen_op_in[3] = { + gen_op_inb_T0_T1, + gen_op_inw_T0_T1, + gen_op_inl_T0_T1, +}; + +static GenOpFunc *gen_op_out[3] = { + gen_op_outb_T0_T1, + gen_op_outw_T0_T1, + gen_op_outl_T0_T1, +}; + +static GenOpFunc *gen_check_io_T0[3] = { + gen_op_check_iob_T0, + gen_op_check_iow_T0, + gen_op_check_iol_T0, +}; + +static GenOpFunc *gen_check_io_DX[3] = { + gen_op_check_iob_DX, + gen_op_check_iow_DX, + gen_op_check_iol_DX, +}; + +static void gen_check_io(DisasContext *s, int ot, int use_dx, target_ulong cur_eip) +{ + if (s->pe && (s->cpl > s->iopl || s->vm86)) { + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_jmp_im(cur_eip); + if (use_dx) + gen_check_io_DX[ot](); + else + gen_check_io_T0[ot](); + } +} + +static inline void gen_movs(DisasContext *s, int ot) +{ + gen_string_movl_A0_ESI(s); + gen_op_ld_T0_A0[ot + s->mem_index](); + gen_string_movl_A0_EDI(s); + gen_op_st_T0_A0[ot + s->mem_index](); + gen_op_movl_T0_Dshift[ot](); +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + gen_op_addq_ESI_T0(); + gen_op_addq_EDI_T0(); + } else +#endif + if (s->aflag) { + gen_op_addl_ESI_T0(); + gen_op_addl_EDI_T0(); + } else { + gen_op_addw_ESI_T0(); + gen_op_addw_EDI_T0(); + } +} + +static inline void gen_update_cc_op(DisasContext *s) +{ + if (s->cc_op != CC_OP_DYNAMIC) { + gen_op_set_cc_op(s->cc_op); + s->cc_op = CC_OP_DYNAMIC; + } +} + +/* XXX: does not work with gdbstub "ice" single step - not a + serious problem */ +static int gen_jz_ecx_string(DisasContext *s, target_ulong next_eip) +{ + int l1, l2; + + l1 = gen_new_label(); + l2 = gen_new_label(); + gen_op_jnz_ecx[s->aflag](l1); + gen_set_label(l2); + gen_jmp_tb(s, next_eip, 1); + gen_set_label(l1); + return l2; +} + +static inline void gen_stos(DisasContext *s, int ot) +{ + gen_op_mov_TN_reg[OT_LONG][0][R_EAX](); + gen_string_movl_A0_EDI(s); + gen_op_st_T0_A0[ot + s->mem_index](); + gen_op_movl_T0_Dshift[ot](); +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + gen_op_addq_EDI_T0(); + } else +#endif + if (s->aflag) { + gen_op_addl_EDI_T0(); + } else { + gen_op_addw_EDI_T0(); + } +} + +static inline void gen_lods(DisasContext *s, int ot) +{ + gen_string_movl_A0_ESI(s); + gen_op_ld_T0_A0[ot + s->mem_index](); + gen_op_mov_reg_T0[ot][R_EAX](); + gen_op_movl_T0_Dshift[ot](); +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + gen_op_addq_ESI_T0(); + } else +#endif + if (s->aflag) { + gen_op_addl_ESI_T0(); + } else { + gen_op_addw_ESI_T0(); + } +} + +static inline void gen_scas(DisasContext *s, int ot) +{ + gen_op_mov_TN_reg[OT_LONG][0][R_EAX](); + gen_string_movl_A0_EDI(s); + gen_op_ld_T1_A0[ot + s->mem_index](); + gen_op_cmpl_T0_T1_cc(); + gen_op_movl_T0_Dshift[ot](); +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + gen_op_addq_EDI_T0(); + } else +#endif + if (s->aflag) { + gen_op_addl_EDI_T0(); + } else { + gen_op_addw_EDI_T0(); + } +} + +static inline void gen_cmps(DisasContext *s, int ot) +{ + gen_string_movl_A0_ESI(s); + gen_op_ld_T0_A0[ot + s->mem_index](); + gen_string_movl_A0_EDI(s); + gen_op_ld_T1_A0[ot + s->mem_index](); + gen_op_cmpl_T0_T1_cc(); + gen_op_movl_T0_Dshift[ot](); +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + gen_op_addq_ESI_T0(); + gen_op_addq_EDI_T0(); + } else +#endif + if (s->aflag) { + gen_op_addl_ESI_T0(); + gen_op_addl_EDI_T0(); + } else { + gen_op_addw_ESI_T0(); + gen_op_addw_EDI_T0(); + } +} + +static inline void gen_ins(DisasContext *s, int ot) +{ + gen_string_movl_A0_EDI(s); + gen_op_movl_T0_0(); + gen_op_st_T0_A0[ot + s->mem_index](); + gen_op_in_DX_T0[ot](); + gen_op_st_T0_A0[ot + s->mem_index](); + gen_op_movl_T0_Dshift[ot](); +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + gen_op_addq_EDI_T0(); + } else +#endif + if (s->aflag) { + gen_op_addl_EDI_T0(); + } else { + gen_op_addw_EDI_T0(); + } +} + +static inline void gen_outs(DisasContext *s, int ot) +{ + gen_string_movl_A0_ESI(s); + gen_op_ld_T0_A0[ot + s->mem_index](); + gen_op_out_DX_T0[ot](); + gen_op_movl_T0_Dshift[ot](); +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + gen_op_addq_ESI_T0(); + } else +#endif + if (s->aflag) { + gen_op_addl_ESI_T0(); + } else { + gen_op_addw_ESI_T0(); + } +} + +/* same method as Valgrind : we generate jumps to current or next + instruction */ +#define GEN_REPZ(op) \ +static inline void gen_repz_ ## op(DisasContext *s, int ot, \ + target_ulong cur_eip, target_ulong next_eip) \ +{ \ + int l2;\ + gen_update_cc_op(s); \ + l2 = gen_jz_ecx_string(s, next_eip); \ + gen_ ## op(s, ot); \ + gen_op_dec_ECX[s->aflag](); \ + /* a loop would cause two single step exceptions if ECX = 1 \ + before rep string_insn */ \ + if (!s->jmp_opt) \ + gen_op_jz_ecx[s->aflag](l2); \ + gen_jmp(s, cur_eip); \ +} + +#define GEN_REPZ2(op) \ +static inline void gen_repz_ ## op(DisasContext *s, int ot, \ + target_ulong cur_eip, \ + target_ulong next_eip, \ + int nz) \ +{ \ + int l2;\ + gen_update_cc_op(s); \ + l2 = gen_jz_ecx_string(s, next_eip); \ + gen_ ## op(s, ot); \ + gen_op_dec_ECX[s->aflag](); \ + gen_op_set_cc_op(CC_OP_SUBB + ot); \ + gen_op_string_jnz_sub[nz][ot](l2);\ + if (!s->jmp_opt) \ + gen_op_jz_ecx[s->aflag](l2); \ + gen_jmp(s, cur_eip); \ +} + +GEN_REPZ(movs) +GEN_REPZ(stos) +GEN_REPZ(lods) +GEN_REPZ(ins) +GEN_REPZ(outs) +GEN_REPZ2(scas) +GEN_REPZ2(cmps) + +enum { + JCC_O, + JCC_B, + JCC_Z, + JCC_BE, + JCC_S, + JCC_P, + JCC_L, + JCC_LE, +}; + +static GenOpFunc1 *gen_jcc_sub[4][8] = { + [OT_BYTE] = { + NULL, + gen_op_jb_subb, + gen_op_jz_subb, + gen_op_jbe_subb, + gen_op_js_subb, + NULL, + gen_op_jl_subb, + gen_op_jle_subb, + }, + [OT_WORD] = { + NULL, + gen_op_jb_subw, + gen_op_jz_subw, + gen_op_jbe_subw, + gen_op_js_subw, + NULL, + gen_op_jl_subw, + gen_op_jle_subw, + }, + [OT_LONG] = { + NULL, + gen_op_jb_subl, + gen_op_jz_subl, + gen_op_jbe_subl, + gen_op_js_subl, + NULL, + gen_op_jl_subl, + gen_op_jle_subl, + }, +#ifdef TARGET_X86_64 + [OT_QUAD] = { + NULL, + BUGGY_64(gen_op_jb_subq), + gen_op_jz_subq, + BUGGY_64(gen_op_jbe_subq), + gen_op_js_subq, + NULL, + BUGGY_64(gen_op_jl_subq), + BUGGY_64(gen_op_jle_subq), + }, +#endif +}; +static GenOpFunc1 *gen_op_loop[3][4] = { + [0] = { + gen_op_loopnzw, + gen_op_loopzw, + gen_op_jnz_ecxw, + }, + [1] = { + gen_op_loopnzl, + gen_op_loopzl, + gen_op_jnz_ecxl, + }, +#ifdef TARGET_X86_64 + [2] = { + gen_op_loopnzq, + gen_op_loopzq, + gen_op_jnz_ecxq, + }, +#endif +}; + +static GenOpFunc *gen_setcc_slow[8] = { + gen_op_seto_T0_cc, + gen_op_setb_T0_cc, + gen_op_setz_T0_cc, + gen_op_setbe_T0_cc, + gen_op_sets_T0_cc, + gen_op_setp_T0_cc, + gen_op_setl_T0_cc, + gen_op_setle_T0_cc, +}; + +static GenOpFunc *gen_setcc_sub[4][8] = { + [OT_BYTE] = { + NULL, + gen_op_setb_T0_subb, + gen_op_setz_T0_subb, + gen_op_setbe_T0_subb, + gen_op_sets_T0_subb, + NULL, + gen_op_setl_T0_subb, + gen_op_setle_T0_subb, + }, + [OT_WORD] = { + NULL, + gen_op_setb_T0_subw, + gen_op_setz_T0_subw, + gen_op_setbe_T0_subw, + gen_op_sets_T0_subw, + NULL, + gen_op_setl_T0_subw, + gen_op_setle_T0_subw, + }, + [OT_LONG] = { + NULL, + gen_op_setb_T0_subl, + gen_op_setz_T0_subl, + gen_op_setbe_T0_subl, + gen_op_sets_T0_subl, + NULL, + gen_op_setl_T0_subl, + gen_op_setle_T0_subl, + }, +#ifdef TARGET_X86_64 + [OT_QUAD] = { + NULL, + gen_op_setb_T0_subq, + gen_op_setz_T0_subq, + gen_op_setbe_T0_subq, + gen_op_sets_T0_subq, + NULL, + gen_op_setl_T0_subq, + gen_op_setle_T0_subq, + }, +#endif +}; + +static GenOpFunc *gen_op_fp_arith_ST0_FT0[8] = { + gen_op_fadd_ST0_FT0, + gen_op_fmul_ST0_FT0, + gen_op_fcom_ST0_FT0, + gen_op_fcom_ST0_FT0, + gen_op_fsub_ST0_FT0, + gen_op_fsubr_ST0_FT0, + gen_op_fdiv_ST0_FT0, + gen_op_fdivr_ST0_FT0, +}; + +/* NOTE the exception in "r" op ordering */ +static GenOpFunc1 *gen_op_fp_arith_STN_ST0[8] = { + gen_op_fadd_STN_ST0, + gen_op_fmul_STN_ST0, + NULL, + NULL, + gen_op_fsubr_STN_ST0, + gen_op_fsub_STN_ST0, + gen_op_fdivr_STN_ST0, + gen_op_fdiv_STN_ST0, +}; + +/* if d == OR_TMP0, it means memory operand (address in A0) */ +static void gen_op(DisasContext *s1, int op, int ot, int d) +{ + GenOpFunc *gen_update_cc; + + if (d != OR_TMP0) { + gen_op_mov_TN_reg[ot][0][d](); + } else { + gen_op_ld_T0_A0[ot + s1->mem_index](); + } + switch(op) { + case OP_ADCL: + case OP_SBBL: + if (s1->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s1->cc_op); + if (d != OR_TMP0) { + gen_op_arithc_T0_T1_cc[ot][op - OP_ADCL](); + gen_op_mov_reg_T0[ot][d](); + } else { + gen_op_arithc_mem_T0_T1_cc[ot + s1->mem_index][op - OP_ADCL](); + } + s1->cc_op = CC_OP_DYNAMIC; + goto the_end; + case OP_ADDL: + gen_op_addl_T0_T1(); + s1->cc_op = CC_OP_ADDB + ot; + gen_update_cc = gen_op_update2_cc; + break; + case OP_SUBL: + gen_op_subl_T0_T1(); + s1->cc_op = CC_OP_SUBB + ot; + gen_update_cc = gen_op_update2_cc; + break; + default: + case OP_ANDL: + case OP_ORL: + case OP_XORL: + gen_op_arith_T0_T1_cc[op](); + s1->cc_op = CC_OP_LOGICB + ot; + gen_update_cc = gen_op_update1_cc; + break; + case OP_CMPL: + gen_op_cmpl_T0_T1_cc(); + s1->cc_op = CC_OP_SUBB + ot; + gen_update_cc = NULL; + break; + } + if (op != OP_CMPL) { + if (d != OR_TMP0) + gen_op_mov_reg_T0[ot][d](); + else + gen_op_st_T0_A0[ot + s1->mem_index](); + } + /* the flags update must happen after the memory write (precise + exception support) */ + if (gen_update_cc) + gen_update_cc(); + the_end: ; +} + +/* if d == OR_TMP0, it means memory operand (address in A0) */ +static void gen_inc(DisasContext *s1, int ot, int d, int c) +{ + if (d != OR_TMP0) + gen_op_mov_TN_reg[ot][0][d](); + else + gen_op_ld_T0_A0[ot + s1->mem_index](); + if (s1->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s1->cc_op); + if (c > 0) { + gen_op_incl_T0(); + s1->cc_op = CC_OP_INCB + ot; + } else { + gen_op_decl_T0(); + s1->cc_op = CC_OP_DECB + ot; + } + if (d != OR_TMP0) + gen_op_mov_reg_T0[ot][d](); + else + gen_op_st_T0_A0[ot + s1->mem_index](); + gen_op_update_inc_cc(); +} + +static void gen_shift(DisasContext *s1, int op, int ot, int d, int s) +{ + if (d != OR_TMP0) + gen_op_mov_TN_reg[ot][0][d](); + else + gen_op_ld_T0_A0[ot + s1->mem_index](); + if (s != OR_TMP1) + gen_op_mov_TN_reg[ot][1][s](); + /* for zero counts, flags are not updated, so must do it dynamically */ + if (s1->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s1->cc_op); + + if (d != OR_TMP0) + gen_op_shift_T0_T1_cc[ot][op](); + else + gen_op_shift_mem_T0_T1_cc[ot + s1->mem_index][op](); + if (d != OR_TMP0) + gen_op_mov_reg_T0[ot][d](); + s1->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */ +} + +static void gen_shifti(DisasContext *s1, int op, int ot, int d, int c) +{ + /* currently not optimized */ + gen_op_movl_T1_im(c); + gen_shift(s1, op, ot, d, OR_TMP1); +} + +static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ptr) +{ + target_long disp; + int havesib; + int base; + int index; + int scale; + int opreg; + int mod, rm, code, override, must_add_seg; + + override = s->override; + must_add_seg = s->addseg; + if (override >= 0) + must_add_seg = 1; + mod = (modrm >> 6) & 3; + rm = modrm & 7; + + if (s->aflag) { + + havesib = 0; + base = rm; + index = 0; + scale = 0; + + if (base == 4) { + havesib = 1; + code = ldub_code(s->pc++); + scale = (code >> 6) & 3; + index = ((code >> 3) & 7) | REX_X(s); + base = (code & 7); + } + base |= REX_B(s); + + switch (mod) { + case 0: + if ((base & 7) == 5) { + base = -1; + disp = (int32_t)ldl_code(s->pc); + s->pc += 4; + if (CODE64(s) && !havesib) { + disp += s->pc + s->rip_offset; + } + } else { + disp = 0; + } + break; + case 1: + disp = (int8_t)ldub_code(s->pc++); + break; + default: + case 2: + disp = ldl_code(s->pc); + s->pc += 4; + break; + } + + if (base >= 0) { + /* for correct popl handling with esp */ + if (base == 4 && s->popl_esp_hack) + disp += s->popl_esp_hack; +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + gen_op_movq_A0_reg[base](); + if (disp != 0) { + if ((int32_t)disp == disp) + gen_op_addq_A0_im(disp); + else + gen_op_addq_A0_im64(disp >> 32, disp); + } + } else +#endif + { + gen_op_movl_A0_reg[base](); + if (disp != 0) + gen_op_addl_A0_im(disp); + } + } else { +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + if ((int32_t)disp == disp) + gen_op_movq_A0_im(disp); + else + gen_op_movq_A0_im64(disp >> 32, disp); + } else +#endif + { + gen_op_movl_A0_im(disp); + } + } + /* XXX: index == 4 is always invalid */ + if (havesib && (index != 4 || scale != 0)) { +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + gen_op_addq_A0_reg_sN[scale][index](); + } else +#endif + { + gen_op_addl_A0_reg_sN[scale][index](); + } + } + if (must_add_seg) { + if (override < 0) { + if (base == R_EBP || base == R_ESP) + override = R_SS; + else + override = R_DS; + } +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + gen_op_addq_A0_seg(offsetof(CPUX86State,segs[override].base)); + } else +#endif + { + gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base)); + } + } + } else { + switch (mod) { + case 0: + if (rm == 6) { + disp = lduw_code(s->pc); + s->pc += 2; + gen_op_movl_A0_im(disp); + rm = 0; /* avoid SS override */ + goto no_rm; + } else { + disp = 0; + } + break; + case 1: + disp = (int8_t)ldub_code(s->pc++); + break; + default: + case 2: + disp = lduw_code(s->pc); + s->pc += 2; + break; + } + switch(rm) { + case 0: + gen_op_movl_A0_reg[R_EBX](); + gen_op_addl_A0_reg_sN[0][R_ESI](); + break; + case 1: + gen_op_movl_A0_reg[R_EBX](); + gen_op_addl_A0_reg_sN[0][R_EDI](); + break; + case 2: + gen_op_movl_A0_reg[R_EBP](); + gen_op_addl_A0_reg_sN[0][R_ESI](); + break; + case 3: + gen_op_movl_A0_reg[R_EBP](); + gen_op_addl_A0_reg_sN[0][R_EDI](); + break; + case 4: + gen_op_movl_A0_reg[R_ESI](); + break; + case 5: + gen_op_movl_A0_reg[R_EDI](); + break; + case 6: + gen_op_movl_A0_reg[R_EBP](); + break; + default: + case 7: + gen_op_movl_A0_reg[R_EBX](); + break; + } + if (disp != 0) + gen_op_addl_A0_im(disp); + gen_op_andl_A0_ffff(); + no_rm: + if (must_add_seg) { + if (override < 0) { + if (rm == 2 || rm == 3 || rm == 6) + override = R_SS; + else + override = R_DS; + } + gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base)); + } + } + + opreg = OR_A0; + disp = 0; + *reg_ptr = opreg; + *offset_ptr = disp; +} + +/* used for LEA and MOV AX, mem */ +static void gen_add_A0_ds_seg(DisasContext *s) +{ + int override, must_add_seg; + must_add_seg = s->addseg; + override = R_DS; + if (s->override >= 0) { + override = s->override; + must_add_seg = 1; + } else { + override = R_DS; + } + if (must_add_seg) { +#ifdef TARGET_X86_64 + if (CODE64(s)) { + gen_op_addq_A0_seg(offsetof(CPUX86State,segs[override].base)); + } else +#endif + { + gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base)); + } + } +} + +/* generate modrm memory load or store of 'reg'. TMP0 is used if reg != + OR_TMP0 */ +static void gen_ldst_modrm(DisasContext *s, int modrm, int ot, int reg, int is_store) +{ + int mod, rm, opreg, disp; + + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + if (mod == 3) { + if (is_store) { + if (reg != OR_TMP0) + gen_op_mov_TN_reg[ot][0][reg](); + gen_op_mov_reg_T0[ot][rm](); + } else { + gen_op_mov_TN_reg[ot][0][rm](); + if (reg != OR_TMP0) + gen_op_mov_reg_T0[ot][reg](); + } + } else { + gen_lea_modrm(s, modrm, &opreg, &disp); + if (is_store) { + if (reg != OR_TMP0) + gen_op_mov_TN_reg[ot][0][reg](); + gen_op_st_T0_A0[ot + s->mem_index](); + } else { + gen_op_ld_T0_A0[ot + s->mem_index](); + if (reg != OR_TMP0) + gen_op_mov_reg_T0[ot][reg](); + } + } +} + +static inline uint32_t insn_get(DisasContext *s, int ot) +{ + uint32_t ret; + + switch(ot) { + case OT_BYTE: + ret = ldub_code(s->pc); + s->pc++; + break; + case OT_WORD: + ret = lduw_code(s->pc); + s->pc += 2; + break; + default: + case OT_LONG: + ret = ldl_code(s->pc); + s->pc += 4; + break; + } + return ret; +} + +static inline int insn_const_size(unsigned int ot) +{ + if (ot <= OT_LONG) + return 1 << ot; + else + return 4; +} + +static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip) +{ + TranslationBlock *tb; + target_ulong pc; + + pc = s->cs_base + eip; + tb = s->tb; + /* NOTE: we handle the case where the TB spans two pages here */ + if ((pc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) || + (pc & TARGET_PAGE_MASK) == ((s->pc - 1) & TARGET_PAGE_MASK)) { + /* jump to same page: we can use a direct jump */ + if (tb_num == 0) + gen_op_goto_tb0(TBPARAM(tb)); + else + gen_op_goto_tb1(TBPARAM(tb)); + gen_jmp_im(eip); + gen_op_movl_T0_im((long)tb + tb_num); + gen_op_exit_tb(); + } else { + /* jump to another page: currently not optimized */ + gen_jmp_im(eip); + gen_eob(s); + } +} + +static inline void gen_jcc(DisasContext *s, int b, + target_ulong val, target_ulong next_eip) +{ + TranslationBlock *tb; + int inv, jcc_op; + GenOpFunc1 *func; + target_ulong tmp; + int l1, l2; + + inv = b & 1; + jcc_op = (b >> 1) & 7; + + if (s->jmp_opt) { + switch(s->cc_op) { + /* we optimize the cmp/jcc case */ + case CC_OP_SUBB: + case CC_OP_SUBW: + case CC_OP_SUBL: + case CC_OP_SUBQ: + func = gen_jcc_sub[s->cc_op - CC_OP_SUBB][jcc_op]; + break; + + /* some jumps are easy to compute */ + case CC_OP_ADDB: + case CC_OP_ADDW: + case CC_OP_ADDL: + case CC_OP_ADDQ: + + case CC_OP_ADCB: + case CC_OP_ADCW: + case CC_OP_ADCL: + case CC_OP_ADCQ: + + case CC_OP_SBBB: + case CC_OP_SBBW: + case CC_OP_SBBL: + case CC_OP_SBBQ: + + case CC_OP_LOGICB: + case CC_OP_LOGICW: + case CC_OP_LOGICL: + case CC_OP_LOGICQ: + + case CC_OP_INCB: + case CC_OP_INCW: + case CC_OP_INCL: + case CC_OP_INCQ: + + case CC_OP_DECB: + case CC_OP_DECW: + case CC_OP_DECL: + case CC_OP_DECQ: + + case CC_OP_SHLB: + case CC_OP_SHLW: + case CC_OP_SHLL: + case CC_OP_SHLQ: + + case CC_OP_SARB: + case CC_OP_SARW: + case CC_OP_SARL: + case CC_OP_SARQ: + switch(jcc_op) { + case JCC_Z: + func = gen_jcc_sub[(s->cc_op - CC_OP_ADDB) % 4][jcc_op]; + break; + case JCC_S: + func = gen_jcc_sub[(s->cc_op - CC_OP_ADDB) % 4][jcc_op]; + break; + default: + func = NULL; + break; + } + break; + default: + func = NULL; + break; + } + + if (s->cc_op != CC_OP_DYNAMIC) { + gen_op_set_cc_op(s->cc_op); + s->cc_op = CC_OP_DYNAMIC; + } + + if (!func) { + gen_setcc_slow[jcc_op](); + func = gen_op_jnz_T0_label; + } + + if (inv) { + tmp = val; + val = next_eip; + next_eip = tmp; + } + tb = s->tb; + + l1 = gen_new_label(); + func(l1); + + gen_goto_tb(s, 0, next_eip); + + gen_set_label(l1); + gen_goto_tb(s, 1, val); + + s->is_jmp = 3; + } else { + + if (s->cc_op != CC_OP_DYNAMIC) { + gen_op_set_cc_op(s->cc_op); + s->cc_op = CC_OP_DYNAMIC; + } + gen_setcc_slow[jcc_op](); + if (inv) { + tmp = val; + val = next_eip; + next_eip = tmp; + } + l1 = gen_new_label(); + l2 = gen_new_label(); + gen_op_jnz_T0_label(l1); + gen_jmp_im(next_eip); + gen_op_jmp_label(l2); + gen_set_label(l1); + gen_jmp_im(val); + gen_set_label(l2); + gen_eob(s); + } +} + +static void gen_setcc(DisasContext *s, int b) +{ + int inv, jcc_op; + GenOpFunc *func; + + inv = b & 1; + jcc_op = (b >> 1) & 7; + switch(s->cc_op) { + /* we optimize the cmp/jcc case */ + case CC_OP_SUBB: + case CC_OP_SUBW: + case CC_OP_SUBL: + case CC_OP_SUBQ: + func = gen_setcc_sub[s->cc_op - CC_OP_SUBB][jcc_op]; + if (!func) + goto slow_jcc; + break; + + /* some jumps are easy to compute */ + case CC_OP_ADDB: + case CC_OP_ADDW: + case CC_OP_ADDL: + case CC_OP_ADDQ: + + case CC_OP_LOGICB: + case CC_OP_LOGICW: + case CC_OP_LOGICL: + case CC_OP_LOGICQ: + + case CC_OP_INCB: + case CC_OP_INCW: + case CC_OP_INCL: + case CC_OP_INCQ: + + case CC_OP_DECB: + case CC_OP_DECW: + case CC_OP_DECL: + case CC_OP_DECQ: + + case CC_OP_SHLB: + case CC_OP_SHLW: + case CC_OP_SHLL: + case CC_OP_SHLQ: + switch(jcc_op) { + case JCC_Z: + func = gen_setcc_sub[(s->cc_op - CC_OP_ADDB) % 4][jcc_op]; + break; + case JCC_S: + func = gen_setcc_sub[(s->cc_op - CC_OP_ADDB) % 4][jcc_op]; + break; + default: + goto slow_jcc; + } + break; + default: + slow_jcc: + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + func = gen_setcc_slow[jcc_op]; + break; + } + func(); + if (inv) { + gen_op_xor_T0_1(); + } +} + +/* move T0 to seg_reg and compute if the CPU state may change. Never + call this function with seg_reg == R_CS */ +static void gen_movl_seg_T0(DisasContext *s, int seg_reg, target_ulong cur_eip) +{ + if (s->pe && !s->vm86) { + /* XXX: optimize by finding processor state dynamically */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_jmp_im(cur_eip); + gen_op_movl_seg_T0(seg_reg); + /* abort translation because the addseg value may change or + because ss32 may change. For R_SS, translation must always + stop as a special handling must be done to disable hardware + interrupts for the next instruction */ + if (seg_reg == R_SS || (s->code32 && seg_reg < R_FS)) + s->is_jmp = 3; + } else { + gen_op_movl_seg_T0_vm(offsetof(CPUX86State,segs[seg_reg])); + if (seg_reg == R_SS) + s->is_jmp = 3; + } +} + +static inline void gen_stack_update(DisasContext *s, int addend) +{ +#ifdef TARGET_X86_64 + if (CODE64(s)) { + if (addend == 8) + gen_op_addq_ESP_8(); + else + gen_op_addq_ESP_im(addend); + } else +#endif + if (s->ss32) { + if (addend == 2) + gen_op_addl_ESP_2(); + else if (addend == 4) + gen_op_addl_ESP_4(); + else + gen_op_addl_ESP_im(addend); + } else { + if (addend == 2) + gen_op_addw_ESP_2(); + else if (addend == 4) + gen_op_addw_ESP_4(); + else + gen_op_addw_ESP_im(addend); + } +} + +/* generate a push. It depends on ss32, addseg and dflag */ +static void gen_push_T0(DisasContext *s) +{ +#ifdef TARGET_X86_64 + if (CODE64(s)) { + gen_op_movq_A0_reg[R_ESP](); + if (s->dflag) { + gen_op_subq_A0_8(); + gen_op_st_T0_A0[OT_QUAD + s->mem_index](); + } else { + gen_op_subq_A0_2(); + gen_op_st_T0_A0[OT_WORD + s->mem_index](); + } + gen_op_movq_ESP_A0(); + } else +#endif + { + gen_op_movl_A0_reg[R_ESP](); + if (!s->dflag) + gen_op_subl_A0_2(); + else + gen_op_subl_A0_4(); + if (s->ss32) { + if (s->addseg) { + gen_op_movl_T1_A0(); + gen_op_addl_A0_SS(); + } + } else { + gen_op_andl_A0_ffff(); + gen_op_movl_T1_A0(); + gen_op_addl_A0_SS(); + } + gen_op_st_T0_A0[s->dflag + 1 + s->mem_index](); + if (s->ss32 && !s->addseg) + gen_op_movl_ESP_A0(); + else + gen_op_mov_reg_T1[s->ss32 + 1][R_ESP](); + } +} + +/* generate a push. It depends on ss32, addseg and dflag */ +/* slower version for T1, only used for call Ev */ +static void gen_push_T1(DisasContext *s) +{ +#ifdef TARGET_X86_64 + if (CODE64(s)) { + gen_op_movq_A0_reg[R_ESP](); + if (s->dflag) { + gen_op_subq_A0_8(); + gen_op_st_T1_A0[OT_QUAD + s->mem_index](); + } else { + gen_op_subq_A0_2(); + gen_op_st_T0_A0[OT_WORD + s->mem_index](); + } + gen_op_movq_ESP_A0(); + } else +#endif + { + gen_op_movl_A0_reg[R_ESP](); + if (!s->dflag) + gen_op_subl_A0_2(); + else + gen_op_subl_A0_4(); + if (s->ss32) { + if (s->addseg) { + gen_op_addl_A0_SS(); + } + } else { + gen_op_andl_A0_ffff(); + gen_op_addl_A0_SS(); + } + gen_op_st_T1_A0[s->dflag + 1 + s->mem_index](); + + if (s->ss32 && !s->addseg) + gen_op_movl_ESP_A0(); + else + gen_stack_update(s, (-2) << s->dflag); + } +} + +/* two step pop is necessary for precise exceptions */ +static void gen_pop_T0(DisasContext *s) +{ +#ifdef TARGET_X86_64 + if (CODE64(s)) { + gen_op_movq_A0_reg[R_ESP](); + gen_op_ld_T0_A0[(s->dflag ? OT_QUAD : OT_WORD) + s->mem_index](); + } else +#endif + { + gen_op_movl_A0_reg[R_ESP](); + if (s->ss32) { + if (s->addseg) + gen_op_addl_A0_SS(); + } else { + gen_op_andl_A0_ffff(); + gen_op_addl_A0_SS(); + } + gen_op_ld_T0_A0[s->dflag + 1 + s->mem_index](); + } +} + +static void gen_pop_update(DisasContext *s) +{ +#ifdef TARGET_X86_64 + if (CODE64(s) && s->dflag) { + gen_stack_update(s, 8); + } else +#endif + { + gen_stack_update(s, 2 << s->dflag); + } +} + +static void gen_stack_A0(DisasContext *s) +{ + gen_op_movl_A0_ESP(); + if (!s->ss32) + gen_op_andl_A0_ffff(); + gen_op_movl_T1_A0(); + if (s->addseg) + gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base)); +} + +/* NOTE: wrap around in 16 bit not fully handled */ +static void gen_pusha(DisasContext *s) +{ + int i; + gen_op_movl_A0_ESP(); + gen_op_addl_A0_im(-16 << s->dflag); + if (!s->ss32) + gen_op_andl_A0_ffff(); + gen_op_movl_T1_A0(); + if (s->addseg) + gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base)); + for(i = 0;i < 8; i++) { + gen_op_mov_TN_reg[OT_LONG][0][7 - i](); + gen_op_st_T0_A0[OT_WORD + s->dflag + s->mem_index](); + gen_op_addl_A0_im(2 << s->dflag); + } + gen_op_mov_reg_T1[OT_WORD + s->ss32][R_ESP](); +} + +/* NOTE: wrap around in 16 bit not fully handled */ +static void gen_popa(DisasContext *s) +{ + int i; + gen_op_movl_A0_ESP(); + if (!s->ss32) + gen_op_andl_A0_ffff(); + gen_op_movl_T1_A0(); + gen_op_addl_T1_im(16 << s->dflag); + if (s->addseg) + gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base)); + for(i = 0;i < 8; i++) { + /* ESP is not reloaded */ + if (i != 3) { + gen_op_ld_T0_A0[OT_WORD + s->dflag + s->mem_index](); + gen_op_mov_reg_T0[OT_WORD + s->dflag][7 - i](); + } + gen_op_addl_A0_im(2 << s->dflag); + } + gen_op_mov_reg_T1[OT_WORD + s->ss32][R_ESP](); +} + +static void gen_enter(DisasContext *s, int esp_addend, int level) +{ + int ot, opsize; + + level &= 0x1f; +#ifdef TARGET_X86_64 + if (CODE64(s)) { + ot = s->dflag ? OT_QUAD : OT_WORD; + opsize = 1 << ot; + + gen_op_movl_A0_ESP(); + gen_op_addq_A0_im(-opsize); + gen_op_movl_T1_A0(); + + /* push bp */ + gen_op_mov_TN_reg[OT_LONG][0][R_EBP](); + gen_op_st_T0_A0[ot + s->mem_index](); + if (level) { + gen_op_enter64_level(level, (ot == OT_QUAD)); + } + gen_op_mov_reg_T1[ot][R_EBP](); + gen_op_addl_T1_im( -esp_addend + (-opsize * level) ); + gen_op_mov_reg_T1[OT_QUAD][R_ESP](); + } else +#endif + { + ot = s->dflag + OT_WORD; + opsize = 2 << s->dflag; + + gen_op_movl_A0_ESP(); + gen_op_addl_A0_im(-opsize); + if (!s->ss32) + gen_op_andl_A0_ffff(); + gen_op_movl_T1_A0(); + if (s->addseg) + gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base)); + /* push bp */ + gen_op_mov_TN_reg[OT_LONG][0][R_EBP](); + gen_op_st_T0_A0[ot + s->mem_index](); + if (level) { + gen_op_enter_level(level, s->dflag); + } + gen_op_mov_reg_T1[ot][R_EBP](); + gen_op_addl_T1_im( -esp_addend + (-opsize * level) ); + gen_op_mov_reg_T1[OT_WORD + s->ss32][R_ESP](); + } +} + +static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip) +{ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_jmp_im(cur_eip); + gen_op_raise_exception(trapno); + s->is_jmp = 3; +} + +/* an interrupt is different from an exception because of the + priviledge checks */ +static void gen_interrupt(DisasContext *s, int intno, + target_ulong cur_eip, target_ulong next_eip) +{ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_jmp_im(cur_eip); + gen_op_raise_interrupt(intno, (int)(next_eip - cur_eip)); + s->is_jmp = 3; +} + +static void gen_debug(DisasContext *s, target_ulong cur_eip) +{ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_jmp_im(cur_eip); + gen_op_debug(); + s->is_jmp = 3; +} + +/* generate a generic end of block. Trace exception is also generated + if needed */ +static void gen_eob(DisasContext *s) +{ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + if (s->tb->flags & HF_INHIBIT_IRQ_MASK) { + gen_op_reset_inhibit_irq(); + } + if (s->singlestep_enabled) { + gen_op_debug(); + } else if (s->tf) { + gen_op_raise_exception(EXCP01_SSTP); + } else { + gen_op_movl_T0_0(); + gen_op_exit_tb(); + } + s->is_jmp = 3; +} + +/* generate a jump to eip. No segment change must happen before as a + direct call to the next block may occur */ +static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num) +{ + if (s->jmp_opt) { + if (s->cc_op != CC_OP_DYNAMIC) { + gen_op_set_cc_op(s->cc_op); + s->cc_op = CC_OP_DYNAMIC; + } + gen_goto_tb(s, tb_num, eip); + s->is_jmp = 3; + } else { + gen_jmp_im(eip); + gen_eob(s); + } +} + +static void gen_jmp(DisasContext *s, target_ulong eip) +{ + gen_jmp_tb(s, eip, 0); +} + +static void gen_movtl_T0_im(target_ulong val) +{ +#ifdef TARGET_X86_64 + if ((int32_t)val == val) { + gen_op_movl_T0_im(val); + } else { + gen_op_movq_T0_im64(val >> 32, val); + } +#else + gen_op_movl_T0_im(val); +#endif +} + +static void gen_movtl_T1_im(target_ulong val) +{ +#ifdef TARGET_X86_64 + if ((int32_t)val == val) { + gen_op_movl_T1_im(val); + } else { + gen_op_movq_T1_im64(val >> 32, val); + } +#else + gen_op_movl_T1_im(val); +#endif +} + +static void gen_add_A0_im(DisasContext *s, int val) +{ +#ifdef TARGET_X86_64 + if (CODE64(s)) + gen_op_addq_A0_im(val); + else +#endif + gen_op_addl_A0_im(val); +} + +static GenOpFunc1 *gen_ldq_env_A0[3] = { + gen_op_ldq_raw_env_A0, +#ifndef CONFIG_USER_ONLY + gen_op_ldq_kernel_env_A0, + gen_op_ldq_user_env_A0, +#endif +}; + +static GenOpFunc1 *gen_stq_env_A0[3] = { + gen_op_stq_raw_env_A0, +#ifndef CONFIG_USER_ONLY + gen_op_stq_kernel_env_A0, + gen_op_stq_user_env_A0, +#endif +}; + +static GenOpFunc1 *gen_ldo_env_A0[3] = { + gen_op_ldo_raw_env_A0, +#ifndef CONFIG_USER_ONLY + gen_op_ldo_kernel_env_A0, + gen_op_ldo_user_env_A0, +#endif +}; + +static GenOpFunc1 *gen_sto_env_A0[3] = { + gen_op_sto_raw_env_A0, +#ifndef CONFIG_USER_ONLY + gen_op_sto_kernel_env_A0, + gen_op_sto_user_env_A0, +#endif +}; + +#define SSE_SPECIAL ((GenOpFunc2 *)1) + +#define MMX_OP2(x) { gen_op_ ## x ## _mmx, gen_op_ ## x ## _xmm } +#define SSE_FOP(x) { gen_op_ ## x ## ps, gen_op_ ## x ## pd, \ + gen_op_ ## x ## ss, gen_op_ ## x ## sd, } + +static GenOpFunc2 *sse_op_table1[256][4] = { + /* pure SSE operations */ + [0x10] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */ + [0x11] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */ + [0x12] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movlps, movlpd, movsldup, movddup */ + [0x13] = { SSE_SPECIAL, SSE_SPECIAL }, /* movlps, movlpd */ + [0x14] = { gen_op_punpckldq_xmm, gen_op_punpcklqdq_xmm }, + [0x15] = { gen_op_punpckhdq_xmm, gen_op_punpckhqdq_xmm }, + [0x16] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movhps, movhpd, movshdup */ + [0x17] = { SSE_SPECIAL, SSE_SPECIAL }, /* movhps, movhpd */ + + [0x28] = { SSE_SPECIAL, SSE_SPECIAL }, /* movaps, movapd */ + [0x29] = { SSE_SPECIAL, SSE_SPECIAL }, /* movaps, movapd */ + [0x2a] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtpi2ps, cvtpi2pd, cvtsi2ss, cvtsi2sd */ + [0x2b] = { SSE_SPECIAL, SSE_SPECIAL }, /* movntps, movntpd */ + [0x2c] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvttps2pi, cvttpd2pi, cvttsd2si, cvttss2si */ + [0x2d] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtps2pi, cvtpd2pi, cvtsd2si, cvtss2si */ + [0x2e] = { gen_op_ucomiss, gen_op_ucomisd }, + [0x2f] = { gen_op_comiss, gen_op_comisd }, + [0x50] = { SSE_SPECIAL, SSE_SPECIAL }, /* movmskps, movmskpd */ + [0x51] = SSE_FOP(sqrt), + [0x52] = { gen_op_rsqrtps, NULL, gen_op_rsqrtss, NULL }, + [0x53] = { gen_op_rcpps, NULL, gen_op_rcpss, NULL }, + [0x54] = { gen_op_pand_xmm, gen_op_pand_xmm }, /* andps, andpd */ + [0x55] = { gen_op_pandn_xmm, gen_op_pandn_xmm }, /* andnps, andnpd */ + [0x56] = { gen_op_por_xmm, gen_op_por_xmm }, /* orps, orpd */ + [0x57] = { gen_op_pxor_xmm, gen_op_pxor_xmm }, /* xorps, xorpd */ + [0x58] = SSE_FOP(add), + [0x59] = SSE_FOP(mul), + [0x5a] = { gen_op_cvtps2pd, gen_op_cvtpd2ps, + gen_op_cvtss2sd, gen_op_cvtsd2ss }, + [0x5b] = { gen_op_cvtdq2ps, gen_op_cvtps2dq, gen_op_cvttps2dq }, + [0x5c] = SSE_FOP(sub), + [0x5d] = SSE_FOP(min), + [0x5e] = SSE_FOP(div), + [0x5f] = SSE_FOP(max), + + [0xc2] = SSE_FOP(cmpeq), + [0xc6] = { (GenOpFunc2 *)gen_op_shufps, (GenOpFunc2 *)gen_op_shufpd }, + + /* MMX ops and their SSE extensions */ + [0x60] = MMX_OP2(punpcklbw), + [0x61] = MMX_OP2(punpcklwd), + [0x62] = MMX_OP2(punpckldq), + [0x63] = MMX_OP2(packsswb), + [0x64] = MMX_OP2(pcmpgtb), + [0x65] = MMX_OP2(pcmpgtw), + [0x66] = MMX_OP2(pcmpgtl), + [0x67] = MMX_OP2(packuswb), + [0x68] = MMX_OP2(punpckhbw), + [0x69] = MMX_OP2(punpckhwd), + [0x6a] = MMX_OP2(punpckhdq), + [0x6b] = MMX_OP2(packssdw), + [0x6c] = { NULL, gen_op_punpcklqdq_xmm }, + [0x6d] = { NULL, gen_op_punpckhqdq_xmm }, + [0x6e] = { SSE_SPECIAL, SSE_SPECIAL }, /* movd mm, ea */ + [0x6f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, , movqdu */ + [0x70] = { (GenOpFunc2 *)gen_op_pshufw_mmx, + (GenOpFunc2 *)gen_op_pshufd_xmm, + (GenOpFunc2 *)gen_op_pshufhw_xmm, + (GenOpFunc2 *)gen_op_pshuflw_xmm }, + [0x71] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftw */ + [0x72] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftd */ + [0x73] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftq */ + [0x74] = MMX_OP2(pcmpeqb), + [0x75] = MMX_OP2(pcmpeqw), + [0x76] = MMX_OP2(pcmpeql), + [0x77] = { SSE_SPECIAL }, /* emms */ + [0x7c] = { NULL, gen_op_haddpd, NULL, gen_op_haddps }, + [0x7d] = { NULL, gen_op_hsubpd, NULL, gen_op_hsubps }, + [0x7e] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movd, movd, , movq */ + [0x7f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, movdqu */ + [0xc4] = { SSE_SPECIAL, SSE_SPECIAL }, /* pinsrw */ + [0xc5] = { SSE_SPECIAL, SSE_SPECIAL }, /* pextrw */ + [0xd0] = { NULL, gen_op_addsubpd, NULL, gen_op_addsubps }, + [0xd1] = MMX_OP2(psrlw), + [0xd2] = MMX_OP2(psrld), + [0xd3] = MMX_OP2(psrlq), + [0xd4] = MMX_OP2(paddq), + [0xd5] = MMX_OP2(pmullw), + [0xd6] = { NULL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, + [0xd7] = { SSE_SPECIAL, SSE_SPECIAL }, /* pmovmskb */ + [0xd8] = MMX_OP2(psubusb), + [0xd9] = MMX_OP2(psubusw), + [0xda] = MMX_OP2(pminub), + [0xdb] = MMX_OP2(pand), + [0xdc] = MMX_OP2(paddusb), + [0xdd] = MMX_OP2(paddusw), + [0xde] = MMX_OP2(pmaxub), + [0xdf] = MMX_OP2(pandn), + [0xe0] = MMX_OP2(pavgb), + [0xe1] = MMX_OP2(psraw), + [0xe2] = MMX_OP2(psrad), + [0xe3] = MMX_OP2(pavgw), + [0xe4] = MMX_OP2(pmulhuw), + [0xe5] = MMX_OP2(pmulhw), + [0xe6] = { NULL, gen_op_cvttpd2dq, gen_op_cvtdq2pd, gen_op_cvtpd2dq }, + [0xe7] = { SSE_SPECIAL , SSE_SPECIAL }, /* movntq, movntq */ + [0xe8] = MMX_OP2(psubsb), + [0xe9] = MMX_OP2(psubsw), + [0xea] = MMX_OP2(pminsw), + [0xeb] = MMX_OP2(por), + [0xec] = MMX_OP2(paddsb), + [0xed] = MMX_OP2(paddsw), + [0xee] = MMX_OP2(pmaxsw), + [0xef] = MMX_OP2(pxor), + [0xf0] = { NULL, NULL, NULL, SSE_SPECIAL }, /* lddqu */ + [0xf1] = MMX_OP2(psllw), + [0xf2] = MMX_OP2(pslld), + [0xf3] = MMX_OP2(psllq), + [0xf4] = MMX_OP2(pmuludq), + [0xf5] = MMX_OP2(pmaddwd), + [0xf6] = MMX_OP2(psadbw), + [0xf7] = MMX_OP2(maskmov), + [0xf8] = MMX_OP2(psubb), + [0xf9] = MMX_OP2(psubw), + [0xfa] = MMX_OP2(psubl), + [0xfb] = MMX_OP2(psubq), + [0xfc] = MMX_OP2(paddb), + [0xfd] = MMX_OP2(paddw), + [0xfe] = MMX_OP2(paddl), +}; + +static GenOpFunc2 *sse_op_table2[3 * 8][2] = { + [0 + 2] = MMX_OP2(psrlw), + [0 + 4] = MMX_OP2(psraw), + [0 + 6] = MMX_OP2(psllw), + [8 + 2] = MMX_OP2(psrld), + [8 + 4] = MMX_OP2(psrad), + [8 + 6] = MMX_OP2(pslld), + [16 + 2] = MMX_OP2(psrlq), + [16 + 3] = { NULL, gen_op_psrldq_xmm }, + [16 + 6] = MMX_OP2(psllq), + [16 + 7] = { NULL, gen_op_pslldq_xmm }, +}; + +static GenOpFunc1 *sse_op_table3[4 * 3] = { + gen_op_cvtsi2ss, + gen_op_cvtsi2sd, + X86_64_ONLY(gen_op_cvtsq2ss), + X86_64_ONLY(gen_op_cvtsq2sd), + + gen_op_cvttss2si, + gen_op_cvttsd2si, + X86_64_ONLY(gen_op_cvttss2sq), + X86_64_ONLY(gen_op_cvttsd2sq), + + gen_op_cvtss2si, + gen_op_cvtsd2si, + X86_64_ONLY(gen_op_cvtss2sq), + X86_64_ONLY(gen_op_cvtsd2sq), +}; + +static GenOpFunc2 *sse_op_table4[8][4] = { + SSE_FOP(cmpeq), + SSE_FOP(cmplt), + SSE_FOP(cmple), + SSE_FOP(cmpunord), + SSE_FOP(cmpneq), + SSE_FOP(cmpnlt), + SSE_FOP(cmpnle), + SSE_FOP(cmpord), +}; + +static void gen_sse(DisasContext *s, int b, target_ulong pc_start, int rex_r) +{ + int b1, op1_offset, op2_offset, is_xmm, val, ot; + int modrm, mod, rm, reg, reg_addr, offset_addr; + GenOpFunc2 *sse_op2; + GenOpFunc3 *sse_op3; + + b &= 0xff; + if (s->prefix & PREFIX_DATA) + b1 = 1; + else if (s->prefix & PREFIX_REPZ) + b1 = 2; + else if (s->prefix & PREFIX_REPNZ) + b1 = 3; + else + b1 = 0; + sse_op2 = sse_op_table1[b][b1]; + if (!sse_op2) + goto illegal_op; + if (b <= 0x5f || b == 0xc6 || b == 0xc2) { + is_xmm = 1; + } else { + if (b1 == 0) { + /* MMX case */ + is_xmm = 0; + } else { + is_xmm = 1; + } + } + /* simple MMX/SSE operation */ + if (s->flags & HF_TS_MASK) { + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + return; + } + if (s->flags & HF_EM_MASK) { + illegal_op: + gen_exception(s, EXCP06_ILLOP, pc_start - s->cs_base); + return; + } + if (is_xmm && !(s->flags & HF_OSFXSR_MASK)) + goto illegal_op; + if (b == 0x77) { + /* emms */ + gen_op_emms(); + return; + } + /* prepare MMX state (XXX: optimize by storing fptt and fptags in + the static cpu state) */ + if (!is_xmm) { + gen_op_enter_mmx(); + } + + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7); + if (is_xmm) + reg |= rex_r; + mod = (modrm >> 6) & 3; + if (sse_op2 == SSE_SPECIAL) { + b |= (b1 << 8); + switch(b) { + case 0x0e7: /* movntq */ + if (mod == 3) + goto illegal_op; + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_stq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,fpregs[reg].mmx)); + break; + case 0x1e7: /* movntdq */ + case 0x02b: /* movntps */ + case 0x12b: /* movntps */ + case 0x3f0: /* lddqu */ + if (mod == 3) + goto illegal_op; + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_sto_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg])); + break; + case 0x6e: /* movd mm, ea */ + gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 0); + gen_op_movl_mm_T0_mmx(offsetof(CPUX86State,fpregs[reg].mmx)); + break; + case 0x16e: /* movd xmm, ea */ + gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 0); + gen_op_movl_mm_T0_xmm(offsetof(CPUX86State,xmm_regs[reg])); + break; + case 0x6f: /* movq mm, ea */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,fpregs[reg].mmx)); + } else { + rm = (modrm & 7); + gen_op_movq(offsetof(CPUX86State,fpregs[reg].mmx), + offsetof(CPUX86State,fpregs[rm].mmx)); + } + break; + case 0x010: /* movups */ + case 0x110: /* movupd */ + case 0x028: /* movaps */ + case 0x128: /* movapd */ + case 0x16f: /* movdqa xmm, ea */ + case 0x26f: /* movdqu xmm, ea */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_ldo_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg])); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movo(offsetof(CPUX86State,xmm_regs[reg]), + offsetof(CPUX86State,xmm_regs[rm])); + } + break; + case 0x210: /* movss xmm, ea */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_ld_T0_A0[OT_LONG + s->mem_index](); + gen_op_movl_env_T0(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); + gen_op_movl_T0_0(); + gen_op_movl_env_T0(offsetof(CPUX86State,xmm_regs[reg].XMM_L(1))); + gen_op_movl_env_T0(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2))); + gen_op_movl_env_T0(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3))); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)), + offsetof(CPUX86State,xmm_regs[rm].XMM_L(0))); + } + break; + case 0x310: /* movsd xmm, ea */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + gen_op_movl_T0_0(); + gen_op_movl_env_T0(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2))); + gen_op_movl_env_T0(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3))); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), + offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); + } + break; + case 0x012: /* movlps */ + case 0x112: /* movlpd */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + } else { + /* movhlps */ + rm = (modrm & 7) | REX_B(s); + gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), + offsetof(CPUX86State,xmm_regs[rm].XMM_Q(1))); + } + break; + case 0x212: /* movsldup */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_ldo_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg])); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)), + offsetof(CPUX86State,xmm_regs[rm].XMM_L(0))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)), + offsetof(CPUX86State,xmm_regs[rm].XMM_L(2))); + } + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)), + offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)), + offsetof(CPUX86State,xmm_regs[reg].XMM_L(2))); + break; + case 0x312: /* movddup */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), + offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); + } + gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)), + offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + break; + case 0x016: /* movhps */ + case 0x116: /* movhpd */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1))); + } else { + /* movlhps */ + rm = (modrm & 7) | REX_B(s); + gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)), + offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); + } + break; + case 0x216: /* movshdup */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_ldo_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg])); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)), + offsetof(CPUX86State,xmm_regs[rm].XMM_L(1))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)), + offsetof(CPUX86State,xmm_regs[rm].XMM_L(3))); + } + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)), + offsetof(CPUX86State,xmm_regs[reg].XMM_L(1))); + gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)), + offsetof(CPUX86State,xmm_regs[reg].XMM_L(3))); + break; + case 0x7e: /* movd ea, mm */ + gen_op_movl_T0_mm_mmx(offsetof(CPUX86State,fpregs[reg].mmx)); + gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 1); + break; + case 0x17e: /* movd ea, xmm */ + gen_op_movl_T0_mm_xmm(offsetof(CPUX86State,xmm_regs[reg])); + gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 1); + break; + case 0x27e: /* movq xmm, ea */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), + offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); + } + gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1))); + break; + case 0x7f: /* movq ea, mm */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_stq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,fpregs[reg].mmx)); + } else { + rm = (modrm & 7); + gen_op_movq(offsetof(CPUX86State,fpregs[rm].mmx), + offsetof(CPUX86State,fpregs[reg].mmx)); + } + break; + case 0x011: /* movups */ + case 0x111: /* movupd */ + case 0x029: /* movaps */ + case 0x129: /* movapd */ + case 0x17f: /* movdqa ea, xmm */ + case 0x27f: /* movdqu ea, xmm */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_sto_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg])); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movo(offsetof(CPUX86State,xmm_regs[rm]), + offsetof(CPUX86State,xmm_regs[reg])); + } + break; + case 0x211: /* movss ea, xmm */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_movl_T0_env(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); + gen_op_st_T0_A0[OT_LONG + s->mem_index](); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movl(offsetof(CPUX86State,xmm_regs[rm].XMM_L(0)), + offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); + } + break; + case 0x311: /* movsd ea, xmm */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_stq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)), + offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + } + break; + case 0x013: /* movlps */ + case 0x113: /* movlpd */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_stq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + } else { + goto illegal_op; + } + break; + case 0x017: /* movhps */ + case 0x117: /* movhpd */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_stq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1))); + } else { + goto illegal_op; + } + break; + case 0x71: /* shift mm, im */ + case 0x72: + case 0x73: + case 0x171: /* shift xmm, im */ + case 0x172: + case 0x173: + val = ldub_code(s->pc++); + if (is_xmm) { + gen_op_movl_T0_im(val); + gen_op_movl_env_T0(offsetof(CPUX86State,xmm_t0.XMM_L(0))); + gen_op_movl_T0_0(); + gen_op_movl_env_T0(offsetof(CPUX86State,xmm_t0.XMM_L(1))); + op1_offset = offsetof(CPUX86State,xmm_t0); + } else { + gen_op_movl_T0_im(val); + gen_op_movl_env_T0(offsetof(CPUX86State,mmx_t0.MMX_L(0))); + gen_op_movl_T0_0(); + gen_op_movl_env_T0(offsetof(CPUX86State,mmx_t0.MMX_L(1))); + op1_offset = offsetof(CPUX86State,mmx_t0); + } + sse_op2 = sse_op_table2[((b - 1) & 3) * 8 + (((modrm >> 3)) & 7)][b1]; + if (!sse_op2) + goto illegal_op; + if (is_xmm) { + rm = (modrm & 7) | REX_B(s); + op2_offset = offsetof(CPUX86State,xmm_regs[rm]); + } else { + rm = (modrm & 7); + op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); + } + sse_op2(op2_offset, op1_offset); + break; + case 0x050: /* movmskps */ + rm = (modrm & 7) | REX_B(s); + gen_op_movmskps(offsetof(CPUX86State,xmm_regs[rm])); + gen_op_mov_reg_T0[OT_LONG][reg](); + break; + case 0x150: /* movmskpd */ + rm = (modrm & 7) | REX_B(s); + gen_op_movmskpd(offsetof(CPUX86State,xmm_regs[rm])); + gen_op_mov_reg_T0[OT_LONG][reg](); + break; + case 0x02a: /* cvtpi2ps */ + case 0x12a: /* cvtpi2pd */ + gen_op_enter_mmx(); + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + op2_offset = offsetof(CPUX86State,mmx_t0); + gen_ldq_env_A0[s->mem_index >> 2](op2_offset); + } else { + rm = (modrm & 7); + op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); + } + op1_offset = offsetof(CPUX86State,xmm_regs[reg]); + switch(b >> 8) { + case 0x0: + gen_op_cvtpi2ps(op1_offset, op2_offset); + break; + default: + case 0x1: + gen_op_cvtpi2pd(op1_offset, op2_offset); + break; + } + break; + case 0x22a: /* cvtsi2ss */ + case 0x32a: /* cvtsi2sd */ + ot = (s->dflag == 2) ? OT_QUAD : OT_LONG; + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); + op1_offset = offsetof(CPUX86State,xmm_regs[reg]); + sse_op_table3[(s->dflag == 2) * 2 + ((b >> 8) - 2)](op1_offset); + break; + case 0x02c: /* cvttps2pi */ + case 0x12c: /* cvttpd2pi */ + case 0x02d: /* cvtps2pi */ + case 0x12d: /* cvtpd2pi */ + gen_op_enter_mmx(); + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + op2_offset = offsetof(CPUX86State,xmm_t0); + gen_ldo_env_A0[s->mem_index >> 2](op2_offset); + } else { + rm = (modrm & 7) | REX_B(s); + op2_offset = offsetof(CPUX86State,xmm_regs[rm]); + } + op1_offset = offsetof(CPUX86State,fpregs[reg & 7].mmx); + switch(b) { + case 0x02c: + gen_op_cvttps2pi(op1_offset, op2_offset); + break; + case 0x12c: + gen_op_cvttpd2pi(op1_offset, op2_offset); + break; + case 0x02d: + gen_op_cvtps2pi(op1_offset, op2_offset); + break; + case 0x12d: + gen_op_cvtpd2pi(op1_offset, op2_offset); + break; + } + break; + case 0x22c: /* cvttss2si */ + case 0x32c: /* cvttsd2si */ + case 0x22d: /* cvtss2si */ + case 0x32d: /* cvtsd2si */ + ot = (s->dflag == 2) ? OT_QUAD : OT_LONG; + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + if ((b >> 8) & 1) { + gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_t0.XMM_Q(0))); + } else { + gen_op_ld_T0_A0[OT_LONG + s->mem_index](); + gen_op_movl_env_T0(offsetof(CPUX86State,xmm_t0.XMM_L(0))); + } + op2_offset = offsetof(CPUX86State,xmm_t0); + } else { + rm = (modrm & 7) | REX_B(s); + op2_offset = offsetof(CPUX86State,xmm_regs[rm]); + } + sse_op_table3[(s->dflag == 2) * 2 + ((b >> 8) - 2) + 4 + + (b & 1) * 4](op2_offset); + gen_op_mov_reg_T0[ot][reg](); + break; + case 0xc4: /* pinsrw */ + case 0x1c4: + gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0); + val = ldub_code(s->pc++); + if (b1) { + val &= 7; + gen_op_pinsrw_xmm(offsetof(CPUX86State,xmm_regs[reg]), val); + } else { + val &= 3; + gen_op_pinsrw_mmx(offsetof(CPUX86State,fpregs[reg].mmx), val); + } + break; + case 0xc5: /* pextrw */ + case 0x1c5: + if (mod != 3) + goto illegal_op; + val = ldub_code(s->pc++); + if (b1) { + val &= 7; + rm = (modrm & 7) | REX_B(s); + gen_op_pextrw_xmm(offsetof(CPUX86State,xmm_regs[rm]), val); + } else { + val &= 3; + rm = (modrm & 7); + gen_op_pextrw_mmx(offsetof(CPUX86State,fpregs[rm].mmx), val); + } + reg = ((modrm >> 3) & 7) | rex_r; + gen_op_mov_reg_T0[OT_LONG][reg](); + break; + case 0x1d6: /* movq ea, xmm */ + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_stq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)), + offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(1))); + } + break; + case 0x2d6: /* movq2dq */ + gen_op_enter_mmx(); + rm = (modrm & 7) | REX_B(s); + gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)), + offsetof(CPUX86State,fpregs[reg & 7].mmx)); + gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(1))); + break; + case 0x3d6: /* movdq2q */ + gen_op_enter_mmx(); + rm = (modrm & 7); + gen_op_movq(offsetof(CPUX86State,fpregs[rm].mmx), + offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); + break; + case 0xd7: /* pmovmskb */ + case 0x1d7: + if (mod != 3) + goto illegal_op; + if (b1) { + rm = (modrm & 7) | REX_B(s); + gen_op_pmovmskb_xmm(offsetof(CPUX86State,xmm_regs[rm])); + } else { + rm = (modrm & 7); + gen_op_pmovmskb_mmx(offsetof(CPUX86State,fpregs[rm].mmx)); + } + reg = ((modrm >> 3) & 7) | rex_r; + gen_op_mov_reg_T0[OT_LONG][reg](); + break; + default: + goto illegal_op; + } + } else { + /* generic MMX or SSE operation */ + if (b == 0xf7) { + /* maskmov : we must prepare A0 */ + if (mod != 3) + goto illegal_op; +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + gen_op_movq_A0_reg[R_EDI](); + } else +#endif + { + gen_op_movl_A0_reg[R_EDI](); + if (s->aflag == 0) + gen_op_andl_A0_ffff(); + } + gen_add_A0_ds_seg(s); + } + if (is_xmm) { + op1_offset = offsetof(CPUX86State,xmm_regs[reg]); + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + op2_offset = offsetof(CPUX86State,xmm_t0); + if (b1 >= 2 && ((b >= 0x50 && b <= 0x5f) || + b == 0xc2)) { + /* specific case for SSE single instructions */ + if (b1 == 2) { + /* 32 bit access */ + gen_op_ld_T0_A0[OT_LONG + s->mem_index](); + gen_op_movl_env_T0(offsetof(CPUX86State,xmm_t0.XMM_L(0))); + } else { + /* 64 bit access */ + gen_ldq_env_A0[s->mem_index >> 2](offsetof(CPUX86State,xmm_t0.XMM_D(0))); + } + } else { + gen_ldo_env_A0[s->mem_index >> 2](op2_offset); + } + } else { + rm = (modrm & 7) | REX_B(s); + op2_offset = offsetof(CPUX86State,xmm_regs[rm]); + } + } else { + op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + op2_offset = offsetof(CPUX86State,mmx_t0); + gen_ldq_env_A0[s->mem_index >> 2](op2_offset); + } else { + rm = (modrm & 7); + op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); + } + } + switch(b) { + case 0x70: /* pshufx insn */ + case 0xc6: /* pshufx insn */ + val = ldub_code(s->pc++); + sse_op3 = (GenOpFunc3 *)sse_op2; + sse_op3(op1_offset, op2_offset, val); + break; + case 0xc2: + /* compare insns */ + val = ldub_code(s->pc++); + if (val >= 8) + goto illegal_op; + sse_op2 = sse_op_table4[val][b1]; + sse_op2(op1_offset, op2_offset); + break; + default: + sse_op2(op1_offset, op2_offset); + break; + } + if (b == 0x2e || b == 0x2f) { + s->cc_op = CC_OP_EFLAGS; + } + } +} + + +/* convert one instruction. s->is_jmp is set if the translation must + be stopped. Return the next pc value */ +static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) +{ + int b, prefixes, aflag, dflag; + int shift, ot; + int modrm, reg, rm, mod, reg_addr, op, opreg, offset_addr, val; + target_ulong next_eip, tval; + int rex_w, rex_r; + + s->pc = pc_start; + prefixes = 0; + aflag = s->code32; + dflag = s->code32; + s->override = -1; + rex_w = -1; + rex_r = 0; +#ifdef TARGET_X86_64 + s->rex_x = 0; + s->rex_b = 0; + x86_64_hregs = 0; +#endif + s->rip_offset = 0; /* for relative ip address */ + next_byte: + b = ldub_code(s->pc); + s->pc++; + /* check prefixes */ +#ifdef TARGET_X86_64 + if (CODE64(s)) { + switch (b) { + case 0xf3: + prefixes |= PREFIX_REPZ; + goto next_byte; + case 0xf2: + prefixes |= PREFIX_REPNZ; + goto next_byte; + case 0xf0: + prefixes |= PREFIX_LOCK; + goto next_byte; + case 0x2e: + s->override = R_CS; + goto next_byte; + case 0x36: + s->override = R_SS; + goto next_byte; + case 0x3e: + s->override = R_DS; + goto next_byte; + case 0x26: + s->override = R_ES; + goto next_byte; + case 0x64: + s->override = R_FS; + goto next_byte; + case 0x65: + s->override = R_GS; + goto next_byte; + case 0x66: + prefixes |= PREFIX_DATA; + goto next_byte; + case 0x67: + prefixes |= PREFIX_ADR; + goto next_byte; + case 0x40 ... 0x4f: + /* REX prefix */ + rex_w = (b >> 3) & 1; + rex_r = (b & 0x4) << 1; + s->rex_x = (b & 0x2) << 2; + REX_B(s) = (b & 0x1) << 3; + x86_64_hregs = 1; /* select uniform byte register addressing */ + goto next_byte; + } + if (rex_w == 1) { + /* 0x66 is ignored if rex.w is set */ + dflag = 2; + } else { + if (prefixes & PREFIX_DATA) + dflag ^= 1; + } + if (!(prefixes & PREFIX_ADR)) + aflag = 2; + } else +#endif + { + switch (b) { + case 0xf3: + prefixes |= PREFIX_REPZ; + goto next_byte; + case 0xf2: + prefixes |= PREFIX_REPNZ; + goto next_byte; + case 0xf0: + prefixes |= PREFIX_LOCK; + goto next_byte; + case 0x2e: + s->override = R_CS; + goto next_byte; + case 0x36: + s->override = R_SS; + goto next_byte; + case 0x3e: + s->override = R_DS; + goto next_byte; + case 0x26: + s->override = R_ES; + goto next_byte; + case 0x64: + s->override = R_FS; + goto next_byte; + case 0x65: + s->override = R_GS; + goto next_byte; + case 0x66: + prefixes |= PREFIX_DATA; + goto next_byte; + case 0x67: + prefixes |= PREFIX_ADR; + goto next_byte; + } + if (prefixes & PREFIX_DATA) + dflag ^= 1; + if (prefixes & PREFIX_ADR) + aflag ^= 1; + } + + s->prefix = prefixes; + s->aflag = aflag; + s->dflag = dflag; + + /* lock generation */ + if (prefixes & PREFIX_LOCK) + gen_op_lock(); + + /* now check op code */ + reswitch: + switch(b) { + case 0x0f: + /**************************/ + /* extended op code */ + b = ldub_code(s->pc++) | 0x100; + goto reswitch; + + /**************************/ + /* arith & logic */ + case 0x00 ... 0x05: + case 0x08 ... 0x0d: + case 0x10 ... 0x15: + case 0x18 ... 0x1d: + case 0x20 ... 0x25: + case 0x28 ... 0x2d: + case 0x30 ... 0x35: + case 0x38 ... 0x3d: + { + int op, f, val; + op = (b >> 3) & 7; + f = (b >> 1) & 3; + + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + + switch(f) { + case 0: /* OP Ev, Gv */ + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + opreg = OR_TMP0; + } else if (op == OP_XORL && rm == reg) { + xor_zero: + /* xor reg, reg optimisation */ + gen_op_movl_T0_0(); + s->cc_op = CC_OP_LOGICB + ot; + gen_op_mov_reg_T0[ot][reg](); + gen_op_update1_cc(); + break; + } else { + opreg = rm; + } + gen_op_mov_TN_reg[ot][1][reg](); + gen_op(s, op, ot, opreg); + break; + case 1: /* OP Gv, Ev */ + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + reg = ((modrm >> 3) & 7) | rex_r; + rm = (modrm & 7) | REX_B(s); + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_ld_T1_A0[ot + s->mem_index](); + } else if (op == OP_XORL && rm == reg) { + goto xor_zero; + } else { + gen_op_mov_TN_reg[ot][1][rm](); + } + gen_op(s, op, ot, reg); + break; + case 2: /* OP A, Iv */ + val = insn_get(s, ot); + gen_op_movl_T1_im(val); + gen_op(s, op, ot, OR_EAX); + break; + } + } + break; + + case 0x80: /* GRP1 */ + case 0x81: + case 0x82: + case 0x83: + { + int val; + + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + op = (modrm >> 3) & 7; + + if (mod != 3) { + if (b == 0x83) + s->rip_offset = 1; + else + s->rip_offset = insn_const_size(ot); + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + opreg = OR_TMP0; + } else { + opreg = rm; + } + + switch(b) { + default: + case 0x80: + case 0x81: + case 0x82: + val = insn_get(s, ot); + break; + case 0x83: + val = (int8_t)insn_get(s, OT_BYTE); + break; + } + gen_op_movl_T1_im(val); + gen_op(s, op, ot, opreg); + } + break; + + /**************************/ + /* inc, dec, and other misc arith */ + case 0x40 ... 0x47: /* inc Gv */ + ot = dflag ? OT_LONG : OT_WORD; + gen_inc(s, ot, OR_EAX + (b & 7), 1); + break; + case 0x48 ... 0x4f: /* dec Gv */ + ot = dflag ? OT_LONG : OT_WORD; + gen_inc(s, ot, OR_EAX + (b & 7), -1); + break; + case 0xf6: /* GRP3 */ + case 0xf7: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + op = (modrm >> 3) & 7; + if (mod != 3) { + if (op == 0) + s->rip_offset = insn_const_size(ot); + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_ld_T0_A0[ot + s->mem_index](); + } else { + gen_op_mov_TN_reg[ot][0][rm](); + } + + switch(op) { + case 0: /* test */ + val = insn_get(s, ot); + gen_op_movl_T1_im(val); + gen_op_testl_T0_T1_cc(); + s->cc_op = CC_OP_LOGICB + ot; + break; + case 2: /* not */ + gen_op_notl_T0(); + if (mod != 3) { + gen_op_st_T0_A0[ot + s->mem_index](); + } else { + gen_op_mov_reg_T0[ot][rm](); + } + break; + case 3: /* neg */ + gen_op_negl_T0(); + if (mod != 3) { + gen_op_st_T0_A0[ot + s->mem_index](); + } else { + gen_op_mov_reg_T0[ot][rm](); + } + gen_op_update_neg_cc(); + s->cc_op = CC_OP_SUBB + ot; + break; + case 4: /* mul */ + switch(ot) { + case OT_BYTE: + gen_op_mulb_AL_T0(); + s->cc_op = CC_OP_MULB; + break; + case OT_WORD: + gen_op_mulw_AX_T0(); + s->cc_op = CC_OP_MULW; + break; + default: + case OT_LONG: + gen_op_mull_EAX_T0(); + s->cc_op = CC_OP_MULL; + break; +#ifdef TARGET_X86_64 + case OT_QUAD: + gen_op_mulq_EAX_T0(); + s->cc_op = CC_OP_MULQ; + break; +#endif + } + break; + case 5: /* imul */ + switch(ot) { + case OT_BYTE: + gen_op_imulb_AL_T0(); + s->cc_op = CC_OP_MULB; + break; + case OT_WORD: + gen_op_imulw_AX_T0(); + s->cc_op = CC_OP_MULW; + break; + default: + case OT_LONG: + gen_op_imull_EAX_T0(); + s->cc_op = CC_OP_MULL; + break; +#ifdef TARGET_X86_64 + case OT_QUAD: + gen_op_imulq_EAX_T0(); + s->cc_op = CC_OP_MULQ; + break; +#endif + } + break; + case 6: /* div */ + switch(ot) { + case OT_BYTE: + gen_jmp_im(pc_start - s->cs_base); + gen_op_divb_AL_T0(); + break; + case OT_WORD: + gen_jmp_im(pc_start - s->cs_base); + gen_op_divw_AX_T0(); + break; + default: + case OT_LONG: + gen_jmp_im(pc_start - s->cs_base); + gen_op_divl_EAX_T0(); + break; +#ifdef TARGET_X86_64 + case OT_QUAD: + gen_jmp_im(pc_start - s->cs_base); + gen_op_divq_EAX_T0(); + break; +#endif + } + break; + case 7: /* idiv */ + switch(ot) { + case OT_BYTE: + gen_jmp_im(pc_start - s->cs_base); + gen_op_idivb_AL_T0(); + break; + case OT_WORD: + gen_jmp_im(pc_start - s->cs_base); + gen_op_idivw_AX_T0(); + break; + default: + case OT_LONG: + gen_jmp_im(pc_start - s->cs_base); + gen_op_idivl_EAX_T0(); + break; +#ifdef TARGET_X86_64 + case OT_QUAD: + gen_jmp_im(pc_start - s->cs_base); + gen_op_idivq_EAX_T0(); + break; +#endif + } + break; + default: + goto illegal_op; + } + break; + + case 0xfe: /* GRP4 */ + case 0xff: /* GRP5 */ + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + op = (modrm >> 3) & 7; + if (op >= 2 && b == 0xfe) { + goto illegal_op; + } + if (CODE64(s)) { + if (op == 2 || op == 4) { + /* operand size for jumps is 64 bit */ + ot = OT_QUAD; + } else if (op == 3 || op == 5) { + /* for call calls, the operand is 16 or 32 bit, even + in long mode */ + ot = dflag ? OT_LONG : OT_WORD; + } else if (op == 6) { + /* default push size is 64 bit */ + ot = dflag ? OT_QUAD : OT_WORD; + } + } + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + if (op >= 2 && op != 3 && op != 5) + gen_op_ld_T0_A0[ot + s->mem_index](); + } else { + gen_op_mov_TN_reg[ot][0][rm](); + } + + switch(op) { + case 0: /* inc Ev */ + if (mod != 3) + opreg = OR_TMP0; + else + opreg = rm; + gen_inc(s, ot, opreg, 1); + break; + case 1: /* dec Ev */ + if (mod != 3) + opreg = OR_TMP0; + else + opreg = rm; + gen_inc(s, ot, opreg, -1); + break; + case 2: /* call Ev */ + /* XXX: optimize if memory (no 'and' is necessary) */ + if (s->dflag == 0) + gen_op_andl_T0_ffff(); + next_eip = s->pc - s->cs_base; + gen_movtl_T1_im(next_eip); + gen_push_T1(s); + gen_op_jmp_T0(); + gen_eob(s); + break; + case 3: /* lcall Ev */ + gen_op_ld_T1_A0[ot + s->mem_index](); + gen_add_A0_im(s, 1 << (ot - OT_WORD + 1)); + gen_op_ldu_T0_A0[OT_WORD + s->mem_index](); + do_lcall: + if (s->pe && !s->vm86) { + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_jmp_im(pc_start - s->cs_base); + gen_op_lcall_protected_T0_T1(dflag, s->pc - pc_start); + } else { + gen_op_lcall_real_T0_T1(dflag, s->pc - s->cs_base); + } + gen_eob(s); + break; + case 4: /* jmp Ev */ + if (s->dflag == 0) + gen_op_andl_T0_ffff(); + gen_op_jmp_T0(); + gen_eob(s); + break; + case 5: /* ljmp Ev */ + gen_op_ld_T1_A0[ot + s->mem_index](); + gen_add_A0_im(s, 1 << (ot - OT_WORD + 1)); + gen_op_ldu_T0_A0[OT_WORD + s->mem_index](); + do_ljmp: + if (s->pe && !s->vm86) { + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_jmp_im(pc_start - s->cs_base); + gen_op_ljmp_protected_T0_T1(s->pc - pc_start); + } else { + gen_op_movl_seg_T0_vm(offsetof(CPUX86State,segs[R_CS])); + gen_op_movl_T0_T1(); + gen_op_jmp_T0(); + } + gen_eob(s); + break; + case 6: /* push Ev */ + gen_push_T0(s); + break; + default: + goto illegal_op; + } + break; + + case 0x84: /* test Ev, Gv */ + case 0x85: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + reg = ((modrm >> 3) & 7) | rex_r; + + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); + gen_op_mov_TN_reg[ot][1][reg](); + gen_op_testl_T0_T1_cc(); + s->cc_op = CC_OP_LOGICB + ot; + break; + + case 0xa8: /* test eAX, Iv */ + case 0xa9: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + val = insn_get(s, ot); + + gen_op_mov_TN_reg[ot][0][OR_EAX](); + gen_op_movl_T1_im(val); + gen_op_testl_T0_T1_cc(); + s->cc_op = CC_OP_LOGICB + ot; + break; + + case 0x98: /* CWDE/CBW */ +#ifdef TARGET_X86_64 + if (dflag == 2) { + gen_op_movslq_RAX_EAX(); + } else +#endif + if (dflag == 1) + gen_op_movswl_EAX_AX(); + else + gen_op_movsbw_AX_AL(); + break; + case 0x99: /* CDQ/CWD */ +#ifdef TARGET_X86_64 + if (dflag == 2) { + gen_op_movsqo_RDX_RAX(); + } else +#endif + if (dflag == 1) + gen_op_movslq_EDX_EAX(); + else + gen_op_movswl_DX_AX(); + break; + case 0x1af: /* imul Gv, Ev */ + case 0x69: /* imul Gv, Ev, I */ + case 0x6b: + ot = dflag + OT_WORD; + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + if (b == 0x69) + s->rip_offset = insn_const_size(ot); + else if (b == 0x6b) + s->rip_offset = 1; + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); + if (b == 0x69) { + val = insn_get(s, ot); + gen_op_movl_T1_im(val); + } else if (b == 0x6b) { + val = (int8_t)insn_get(s, OT_BYTE); + gen_op_movl_T1_im(val); + } else { + gen_op_mov_TN_reg[ot][1][reg](); + } + +#ifdef TARGET_X86_64 + if (ot == OT_QUAD) { + gen_op_imulq_T0_T1(); + } else +#endif + if (ot == OT_LONG) { + gen_op_imull_T0_T1(); + } else { + gen_op_imulw_T0_T1(); + } + gen_op_mov_reg_T0[ot][reg](); + s->cc_op = CC_OP_MULB + ot; + break; + case 0x1c0: + case 0x1c1: /* xadd Ev, Gv */ + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + if (mod == 3) { + rm = (modrm & 7) | REX_B(s); + gen_op_mov_TN_reg[ot][0][reg](); + gen_op_mov_TN_reg[ot][1][rm](); + gen_op_addl_T0_T1(); + gen_op_mov_reg_T1[ot][reg](); + gen_op_mov_reg_T0[ot][rm](); + } else { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_mov_TN_reg[ot][0][reg](); + gen_op_ld_T1_A0[ot + s->mem_index](); + gen_op_addl_T0_T1(); + gen_op_st_T0_A0[ot + s->mem_index](); + gen_op_mov_reg_T1[ot][reg](); + } + gen_op_update2_cc(); + s->cc_op = CC_OP_ADDB + ot; + break; + case 0x1b0: + case 0x1b1: /* cmpxchg Ev, Gv */ + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + gen_op_mov_TN_reg[ot][1][reg](); + if (mod == 3) { + rm = (modrm & 7) | REX_B(s); + gen_op_mov_TN_reg[ot][0][rm](); + gen_op_cmpxchg_T0_T1_EAX_cc[ot](); + gen_op_mov_reg_T0[ot][rm](); + } else { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_ld_T0_A0[ot + s->mem_index](); + gen_op_cmpxchg_mem_T0_T1_EAX_cc[ot + s->mem_index](); + } + s->cc_op = CC_OP_SUBB + ot; + break; + case 0x1c7: /* cmpxchg8b */ + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + if (mod == 3) + goto illegal_op; + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_cmpxchg8b(); + s->cc_op = CC_OP_EFLAGS; + break; + + /**************************/ + /* push/pop */ + case 0x50 ... 0x57: /* push */ + gen_op_mov_TN_reg[OT_LONG][0][(b & 7) | REX_B(s)](); + gen_push_T0(s); + break; + case 0x58 ... 0x5f: /* pop */ + if (CODE64(s)) { + ot = dflag ? OT_QUAD : OT_WORD; + } else { + ot = dflag + OT_WORD; + } + gen_pop_T0(s); + /* NOTE: order is important for pop %sp */ + gen_pop_update(s); + gen_op_mov_reg_T0[ot][(b & 7) | REX_B(s)](); + break; + case 0x60: /* pusha */ + if (CODE64(s)) + goto illegal_op; + gen_pusha(s); + break; + case 0x61: /* popa */ + if (CODE64(s)) + goto illegal_op; + gen_popa(s); + break; + case 0x68: /* push Iv */ + case 0x6a: + if (CODE64(s)) { + ot = dflag ? OT_QUAD : OT_WORD; + } else { + ot = dflag + OT_WORD; + } + if (b == 0x68) + val = insn_get(s, ot); + else + val = (int8_t)insn_get(s, OT_BYTE); + gen_op_movl_T0_im(val); + gen_push_T0(s); + break; + case 0x8f: /* pop Ev */ + if (CODE64(s)) { + ot = dflag ? OT_QUAD : OT_WORD; + } else { + ot = dflag + OT_WORD; + } + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + gen_pop_T0(s); + if (mod == 3) { + /* NOTE: order is important for pop %sp */ + gen_pop_update(s); + rm = (modrm & 7) | REX_B(s); + gen_op_mov_reg_T0[ot][rm](); + } else { + /* NOTE: order is important too for MMU exceptions */ + s->popl_esp_hack = 1 << ot; + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1); + s->popl_esp_hack = 0; + gen_pop_update(s); + } + break; + case 0xc8: /* enter */ + { + int level; + val = lduw_code(s->pc); + s->pc += 2; + level = ldub_code(s->pc++); + gen_enter(s, val, level); + } + break; + case 0xc9: /* leave */ + /* XXX: exception not precise (ESP is updated before potential exception) */ + if (CODE64(s)) { + gen_op_mov_TN_reg[OT_QUAD][0][R_EBP](); + gen_op_mov_reg_T0[OT_QUAD][R_ESP](); + } else if (s->ss32) { + gen_op_mov_TN_reg[OT_LONG][0][R_EBP](); + gen_op_mov_reg_T0[OT_LONG][R_ESP](); + } else { + gen_op_mov_TN_reg[OT_WORD][0][R_EBP](); + gen_op_mov_reg_T0[OT_WORD][R_ESP](); + } + gen_pop_T0(s); + if (CODE64(s)) { + ot = dflag ? OT_QUAD : OT_WORD; + } else { + ot = dflag + OT_WORD; + } + gen_op_mov_reg_T0[ot][R_EBP](); + gen_pop_update(s); + break; + case 0x06: /* push es */ + case 0x0e: /* push cs */ + case 0x16: /* push ss */ + case 0x1e: /* push ds */ + if (CODE64(s)) + goto illegal_op; + gen_op_movl_T0_seg(b >> 3); + gen_push_T0(s); + break; + case 0x1a0: /* push fs */ + case 0x1a8: /* push gs */ + gen_op_movl_T0_seg((b >> 3) & 7); + gen_push_T0(s); + break; + case 0x07: /* pop es */ + case 0x17: /* pop ss */ + case 0x1f: /* pop ds */ + if (CODE64(s)) + goto illegal_op; + reg = b >> 3; + gen_pop_T0(s); + gen_movl_seg_T0(s, reg, pc_start - s->cs_base); + gen_pop_update(s); + if (reg == R_SS) { + /* if reg == SS, inhibit interrupts/trace. */ + /* If several instructions disable interrupts, only the + _first_ does it */ + if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK)) + gen_op_set_inhibit_irq(); + s->tf = 0; + } + if (s->is_jmp) { + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + } + break; + case 0x1a1: /* pop fs */ + case 0x1a9: /* pop gs */ + gen_pop_T0(s); + gen_movl_seg_T0(s, (b >> 3) & 7, pc_start - s->cs_base); + gen_pop_update(s); + if (s->is_jmp) { + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + } + break; + + /**************************/ + /* mov */ + case 0x88: + case 0x89: /* mov Gv, Ev */ + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + + /* generate a generic store */ + gen_ldst_modrm(s, modrm, ot, reg, 1); + break; + case 0xc6: + case 0xc7: /* mov Ev, Iv */ + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + if (mod != 3) { + s->rip_offset = insn_const_size(ot); + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + } + val = insn_get(s, ot); + gen_op_movl_T0_im(val); + if (mod != 3) + gen_op_st_T0_A0[ot + s->mem_index](); + else + gen_op_mov_reg_T0[ot][(modrm & 7) | REX_B(s)](); + break; + case 0x8a: + case 0x8b: /* mov Ev, Gv */ + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = OT_WORD + dflag; + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); + gen_op_mov_reg_T0[ot][reg](); + break; + case 0x8e: /* mov seg, Gv */ + modrm = ldub_code(s->pc++); + reg = (modrm >> 3) & 7; + if (reg >= 6 || reg == R_CS) + goto illegal_op; + gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0); + gen_movl_seg_T0(s, reg, pc_start - s->cs_base); + if (reg == R_SS) { + /* if reg == SS, inhibit interrupts/trace */ + /* If several instructions disable interrupts, only the + _first_ does it */ + if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK)) + gen_op_set_inhibit_irq(); + s->tf = 0; + } + if (s->is_jmp) { + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + } + break; + case 0x8c: /* mov Gv, seg */ + modrm = ldub_code(s->pc++); + reg = (modrm >> 3) & 7; + mod = (modrm >> 6) & 3; + if (reg >= 6) + goto illegal_op; + gen_op_movl_T0_seg(reg); + if (mod == 3) + ot = OT_WORD + dflag; + else + ot = OT_WORD; + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1); + break; + + case 0x1b6: /* movzbS Gv, Eb */ + case 0x1b7: /* movzwS Gv, Eb */ + case 0x1be: /* movsbS Gv, Eb */ + case 0x1bf: /* movswS Gv, Eb */ + { + int d_ot; + /* d_ot is the size of destination */ + d_ot = dflag + OT_WORD; + /* ot is the size of source */ + ot = (b & 1) + OT_BYTE; + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + + if (mod == 3) { + gen_op_mov_TN_reg[ot][0][rm](); + switch(ot | (b & 8)) { + case OT_BYTE: + gen_op_movzbl_T0_T0(); + break; + case OT_BYTE | 8: + gen_op_movsbl_T0_T0(); + break; + case OT_WORD: + gen_op_movzwl_T0_T0(); + break; + default: + case OT_WORD | 8: + gen_op_movswl_T0_T0(); + break; + } + gen_op_mov_reg_T0[d_ot][reg](); + } else { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + if (b & 8) { + gen_op_lds_T0_A0[ot + s->mem_index](); + } else { + gen_op_ldu_T0_A0[ot + s->mem_index](); + } + gen_op_mov_reg_T0[d_ot][reg](); + } + } + break; + + case 0x8d: /* lea */ + ot = dflag + OT_WORD; + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + if (mod == 3) + goto illegal_op; + reg = ((modrm >> 3) & 7) | rex_r; + /* we must ensure that no segment is added */ + s->override = -1; + val = s->addseg; + s->addseg = 0; + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + s->addseg = val; + gen_op_mov_reg_A0[ot - OT_WORD][reg](); + break; + + case 0xa0: /* mov EAX, Ov */ + case 0xa1: + case 0xa2: /* mov Ov, EAX */ + case 0xa3: + { + target_ulong offset_addr; + + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + offset_addr = ldq_code(s->pc); + s->pc += 8; + if (offset_addr == (int32_t)offset_addr) + gen_op_movq_A0_im(offset_addr); + else + gen_op_movq_A0_im64(offset_addr >> 32, offset_addr); + } else +#endif + { + if (s->aflag) { + offset_addr = insn_get(s, OT_LONG); + } else { + offset_addr = insn_get(s, OT_WORD); + } + gen_op_movl_A0_im(offset_addr); + } + gen_add_A0_ds_seg(s); + if ((b & 2) == 0) { + gen_op_ld_T0_A0[ot + s->mem_index](); + gen_op_mov_reg_T0[ot][R_EAX](); + } else { + gen_op_mov_TN_reg[ot][0][R_EAX](); + gen_op_st_T0_A0[ot + s->mem_index](); + } + } + break; + case 0xd7: /* xlat */ +#ifdef TARGET_X86_64 + if (s->aflag == 2) { + gen_op_movq_A0_reg[R_EBX](); + gen_op_addq_A0_AL(); + } else +#endif + { + gen_op_movl_A0_reg[R_EBX](); + gen_op_addl_A0_AL(); + if (s->aflag == 0) + gen_op_andl_A0_ffff(); + } + gen_add_A0_ds_seg(s); + gen_op_ldu_T0_A0[OT_BYTE + s->mem_index](); + gen_op_mov_reg_T0[OT_BYTE][R_EAX](); + break; + case 0xb0 ... 0xb7: /* mov R, Ib */ + val = insn_get(s, OT_BYTE); + gen_op_movl_T0_im(val); + gen_op_mov_reg_T0[OT_BYTE][(b & 7) | REX_B(s)](); + break; + case 0xb8 ... 0xbf: /* mov R, Iv */ +#ifdef TARGET_X86_64 + if (dflag == 2) { + uint64_t tmp; + /* 64 bit case */ + tmp = ldq_code(s->pc); + s->pc += 8; + reg = (b & 7) | REX_B(s); + gen_movtl_T0_im(tmp); + gen_op_mov_reg_T0[OT_QUAD][reg](); + } else +#endif + { + ot = dflag ? OT_LONG : OT_WORD; + val = insn_get(s, ot); + reg = (b & 7) | REX_B(s); + gen_op_movl_T0_im(val); + gen_op_mov_reg_T0[ot][reg](); + } + break; + + case 0x91 ... 0x97: /* xchg R, EAX */ + ot = dflag + OT_WORD; + reg = (b & 7) | REX_B(s); + rm = R_EAX; + goto do_xchg_reg; + case 0x86: + case 0x87: /* xchg Ev, Gv */ + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + if (mod == 3) { + rm = (modrm & 7) | REX_B(s); + do_xchg_reg: + gen_op_mov_TN_reg[ot][0][reg](); + gen_op_mov_TN_reg[ot][1][rm](); + gen_op_mov_reg_T0[ot][rm](); + gen_op_mov_reg_T1[ot][reg](); + } else { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_mov_TN_reg[ot][0][reg](); + /* for xchg, lock is implicit */ + if (!(prefixes & PREFIX_LOCK)) + gen_op_lock(); + gen_op_ld_T1_A0[ot + s->mem_index](); + gen_op_st_T0_A0[ot + s->mem_index](); + if (!(prefixes & PREFIX_LOCK)) + gen_op_unlock(); + gen_op_mov_reg_T1[ot][reg](); + } + break; + case 0xc4: /* les Gv */ + if (CODE64(s)) + goto illegal_op; + op = R_ES; + goto do_lxx; + case 0xc5: /* lds Gv */ + if (CODE64(s)) + goto illegal_op; + op = R_DS; + goto do_lxx; + case 0x1b2: /* lss Gv */ + op = R_SS; + goto do_lxx; + case 0x1b4: /* lfs Gv */ + op = R_FS; + goto do_lxx; + case 0x1b5: /* lgs Gv */ + op = R_GS; + do_lxx: + ot = dflag ? OT_LONG : OT_WORD; + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + if (mod == 3) + goto illegal_op; + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_ld_T1_A0[ot + s->mem_index](); + gen_add_A0_im(s, 1 << (ot - OT_WORD + 1)); + /* load the segment first to handle exceptions properly */ + gen_op_ldu_T0_A0[OT_WORD + s->mem_index](); + gen_movl_seg_T0(s, op, pc_start - s->cs_base); + /* then put the data */ + gen_op_mov_reg_T1[ot][reg](); + if (s->is_jmp) { + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + } + break; + + /************************/ + /* shifts */ + case 0xc0: + case 0xc1: + /* shift Ev,Ib */ + shift = 2; + grp2: + { + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + op = (modrm >> 3) & 7; + + if (mod != 3) { + if (shift == 2) { + s->rip_offset = 1; + } + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + opreg = OR_TMP0; + } else { + opreg = (modrm & 7) | REX_B(s); + } + + /* simpler op */ + if (shift == 0) { + gen_shift(s, op, ot, opreg, OR_ECX); + } else { + if (shift == 2) { + shift = ldub_code(s->pc++); + } + gen_shifti(s, op, ot, opreg, shift); + } + } + break; + case 0xd0: + case 0xd1: + /* shift Ev,1 */ + shift = 1; + goto grp2; + case 0xd2: + case 0xd3: + /* shift Ev,cl */ + shift = 0; + goto grp2; + + case 0x1a4: /* shld imm */ + op = 0; + shift = 1; + goto do_shiftd; + case 0x1a5: /* shld cl */ + op = 0; + shift = 0; + goto do_shiftd; + case 0x1ac: /* shrd imm */ + op = 1; + shift = 1; + goto do_shiftd; + case 0x1ad: /* shrd cl */ + op = 1; + shift = 0; + do_shiftd: + ot = dflag + OT_WORD; + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + reg = ((modrm >> 3) & 7) | rex_r; + + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_ld_T0_A0[ot + s->mem_index](); + } else { + gen_op_mov_TN_reg[ot][0][rm](); + } + gen_op_mov_TN_reg[ot][1][reg](); + + if (shift) { + val = ldub_code(s->pc++); + if (ot == OT_QUAD) + val &= 0x3f; + else + val &= 0x1f; + if (val) { + if (mod == 3) + gen_op_shiftd_T0_T1_im_cc[ot][op](val); + else + gen_op_shiftd_mem_T0_T1_im_cc[ot + s->mem_index][op](val); + if (op == 0 && ot != OT_WORD) + s->cc_op = CC_OP_SHLB + ot; + else + s->cc_op = CC_OP_SARB + ot; + } + } else { + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + if (mod == 3) + gen_op_shiftd_T0_T1_ECX_cc[ot][op](); + else + gen_op_shiftd_mem_T0_T1_ECX_cc[ot + s->mem_index][op](); + s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */ + } + if (mod == 3) { + gen_op_mov_reg_T0[ot][rm](); + } + break; + + /************************/ + /* floats */ + case 0xd8 ... 0xdf: + if (s->flags & (HF_EM_MASK | HF_TS_MASK)) { + /* if CR0.EM or CR0.TS are set, generate an FPU exception */ + /* XXX: what to do if illegal op ? */ + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + break; + } + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + rm = modrm & 7; + op = ((b & 7) << 3) | ((modrm >> 3) & 7); + if (mod != 3) { + /* memory op */ + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + switch(op) { + case 0x00 ... 0x07: /* fxxxs */ + case 0x10 ... 0x17: /* fixxxl */ + case 0x20 ... 0x27: /* fxxxl */ + case 0x30 ... 0x37: /* fixxx */ + { + int op1; + op1 = op & 7; + + switch(op >> 4) { + case 0: + gen_op_flds_FT0_A0(); + break; + case 1: + gen_op_fildl_FT0_A0(); + break; + case 2: + gen_op_fldl_FT0_A0(); + break; + case 3: + default: + gen_op_fild_FT0_A0(); + break; + } + + gen_op_fp_arith_ST0_FT0[op1](); + if (op1 == 3) { + /* fcomp needs pop */ + gen_op_fpop(); + } + } + break; + case 0x08: /* flds */ + case 0x0a: /* fsts */ + case 0x0b: /* fstps */ + case 0x18 ... 0x1b: /* fildl, fisttpl, fistl, fistpl */ + case 0x28 ... 0x2b: /* fldl, fisttpll, fstl, fstpl */ + case 0x38 ... 0x3b: /* filds, fisttps, fists, fistps */ + switch(op & 7) { + case 0: + switch(op >> 4) { + case 0: + gen_op_flds_ST0_A0(); + break; + case 1: + gen_op_fildl_ST0_A0(); + break; + case 2: + gen_op_fldl_ST0_A0(); + break; + case 3: + default: + gen_op_fild_ST0_A0(); + break; + } + break; + case 1: + switch(op >> 4) { + case 1: + gen_op_fisttl_ST0_A0(); + break; + case 2: + gen_op_fisttll_ST0_A0(); + break; + case 3: + default: + gen_op_fistt_ST0_A0(); + } + gen_op_fpop(); + break; + default: + switch(op >> 4) { + case 0: + gen_op_fsts_ST0_A0(); + break; + case 1: + gen_op_fistl_ST0_A0(); + break; + case 2: + gen_op_fstl_ST0_A0(); + break; + case 3: + default: + gen_op_fist_ST0_A0(); + break; + } + if ((op & 7) == 3) + gen_op_fpop(); + break; + } + break; + case 0x0c: /* fldenv mem */ + gen_op_fldenv_A0(s->dflag); + break; + case 0x0d: /* fldcw mem */ + gen_op_fldcw_A0(); + break; + case 0x0e: /* fnstenv mem */ + gen_op_fnstenv_A0(s->dflag); + break; + case 0x0f: /* fnstcw mem */ + gen_op_fnstcw_A0(); + break; + case 0x1d: /* fldt mem */ + gen_op_fldt_ST0_A0(); + break; + case 0x1f: /* fstpt mem */ + gen_op_fstt_ST0_A0(); + gen_op_fpop(); + break; + case 0x2c: /* frstor mem */ + gen_op_frstor_A0(s->dflag); + break; + case 0x2e: /* fnsave mem */ + gen_op_fnsave_A0(s->dflag); + break; + case 0x2f: /* fnstsw mem */ + gen_op_fnstsw_A0(); + break; + case 0x3c: /* fbld */ + gen_op_fbld_ST0_A0(); + break; + case 0x3e: /* fbstp */ + gen_op_fbst_ST0_A0(); + gen_op_fpop(); + break; + case 0x3d: /* fildll */ + gen_op_fildll_ST0_A0(); + break; + case 0x3f: /* fistpll */ + gen_op_fistll_ST0_A0(); + gen_op_fpop(); + break; + default: + goto illegal_op; + } + } else { + /* register float ops */ + opreg = rm; + + switch(op) { + case 0x08: /* fld sti */ + gen_op_fpush(); + gen_op_fmov_ST0_STN((opreg + 1) & 7); + break; + case 0x09: /* fxchg sti */ + case 0x29: /* fxchg4 sti, undocumented op */ + case 0x39: /* fxchg7 sti, undocumented op */ + gen_op_fxchg_ST0_STN(opreg); + break; + case 0x0a: /* grp d9/2 */ + switch(rm) { + case 0: /* fnop */ + /* check exceptions (FreeBSD FPU probe) */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_jmp_im(pc_start - s->cs_base); + gen_op_fwait(); + break; + default: + goto illegal_op; + } + break; + case 0x0c: /* grp d9/4 */ + switch(rm) { + case 0: /* fchs */ + gen_op_fchs_ST0(); + break; + case 1: /* fabs */ + gen_op_fabs_ST0(); + break; + case 4: /* ftst */ + gen_op_fldz_FT0(); + gen_op_fcom_ST0_FT0(); + break; + case 5: /* fxam */ + gen_op_fxam_ST0(); + break; + default: + goto illegal_op; + } + break; + case 0x0d: /* grp d9/5 */ + { + switch(rm) { + case 0: + gen_op_fpush(); + gen_op_fld1_ST0(); + break; + case 1: + gen_op_fpush(); + gen_op_fldl2t_ST0(); + break; + case 2: + gen_op_fpush(); + gen_op_fldl2e_ST0(); + break; + case 3: + gen_op_fpush(); + gen_op_fldpi_ST0(); + break; + case 4: + gen_op_fpush(); + gen_op_fldlg2_ST0(); + break; + case 5: + gen_op_fpush(); + gen_op_fldln2_ST0(); + break; + case 6: + gen_op_fpush(); + gen_op_fldz_ST0(); + break; + default: + goto illegal_op; + } + } + break; + case 0x0e: /* grp d9/6 */ + switch(rm) { + case 0: /* f2xm1 */ + gen_op_f2xm1(); + break; + case 1: /* fyl2x */ + gen_op_fyl2x(); + break; + case 2: /* fptan */ + gen_op_fptan(); + break; + case 3: /* fpatan */ + gen_op_fpatan(); + break; + case 4: /* fxtract */ + gen_op_fxtract(); + break; + case 5: /* fprem1 */ + gen_op_fprem1(); + break; + case 6: /* fdecstp */ + gen_op_fdecstp(); + break; + default: + case 7: /* fincstp */ + gen_op_fincstp(); + break; + } + break; + case 0x0f: /* grp d9/7 */ + switch(rm) { + case 0: /* fprem */ + gen_op_fprem(); + break; + case 1: /* fyl2xp1 */ + gen_op_fyl2xp1(); + break; + case 2: /* fsqrt */ + gen_op_fsqrt(); + break; + case 3: /* fsincos */ + gen_op_fsincos(); + break; + case 5: /* fscale */ + gen_op_fscale(); + break; + case 4: /* frndint */ + gen_op_frndint(); + break; + case 6: /* fsin */ + gen_op_fsin(); + break; + default: + case 7: /* fcos */ + gen_op_fcos(); + break; + } + break; + case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */ + case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */ + case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */ + { + int op1; + + op1 = op & 7; + if (op >= 0x20) { + gen_op_fp_arith_STN_ST0[op1](opreg); + if (op >= 0x30) + gen_op_fpop(); + } else { + gen_op_fmov_FT0_STN(opreg); + gen_op_fp_arith_ST0_FT0[op1](); + } + } + break; + case 0x02: /* fcom */ + case 0x22: /* fcom2, undocumented op */ + gen_op_fmov_FT0_STN(opreg); + gen_op_fcom_ST0_FT0(); + break; + case 0x03: /* fcomp */ + case 0x23: /* fcomp3, undocumented op */ + case 0x32: /* fcomp5, undocumented op */ + gen_op_fmov_FT0_STN(opreg); + gen_op_fcom_ST0_FT0(); + gen_op_fpop(); + break; + case 0x15: /* da/5 */ + switch(rm) { + case 1: /* fucompp */ + gen_op_fmov_FT0_STN(1); + gen_op_fucom_ST0_FT0(); + gen_op_fpop(); + gen_op_fpop(); + break; + default: + goto illegal_op; + } + break; + case 0x1c: + switch(rm) { + case 0: /* feni (287 only, just do nop here) */ + break; + case 1: /* fdisi (287 only, just do nop here) */ + break; + case 2: /* fclex */ + gen_op_fclex(); + break; + case 3: /* fninit */ + gen_op_fninit(); + break; + case 4: /* fsetpm (287 only, just do nop here) */ + break; + default: + goto illegal_op; + } + break; + case 0x1d: /* fucomi */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_fmov_FT0_STN(opreg); + gen_op_fucomi_ST0_FT0(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0x1e: /* fcomi */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_fmov_FT0_STN(opreg); + gen_op_fcomi_ST0_FT0(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0x28: /* ffree sti */ + gen_op_ffree_STN(opreg); + break; + case 0x2a: /* fst sti */ + gen_op_fmov_STN_ST0(opreg); + break; + case 0x2b: /* fstp sti */ + case 0x0b: /* fstp1 sti, undocumented op */ + case 0x3a: /* fstp8 sti, undocumented op */ + case 0x3b: /* fstp9 sti, undocumented op */ + gen_op_fmov_STN_ST0(opreg); + gen_op_fpop(); + break; + case 0x2c: /* fucom st(i) */ + gen_op_fmov_FT0_STN(opreg); + gen_op_fucom_ST0_FT0(); + break; + case 0x2d: /* fucomp st(i) */ + gen_op_fmov_FT0_STN(opreg); + gen_op_fucom_ST0_FT0(); + gen_op_fpop(); + break; + case 0x33: /* de/3 */ + switch(rm) { + case 1: /* fcompp */ + gen_op_fmov_FT0_STN(1); + gen_op_fcom_ST0_FT0(); + gen_op_fpop(); + gen_op_fpop(); + break; + default: + goto illegal_op; + } + break; + case 0x38: /* ffreep sti, undocumented op */ + gen_op_ffree_STN(opreg); + gen_op_fpop(); + break; + case 0x3c: /* df/4 */ + switch(rm) { + case 0: + gen_op_fnstsw_EAX(); + break; + default: + goto illegal_op; + } + break; + case 0x3d: /* fucomip */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_fmov_FT0_STN(opreg); + gen_op_fucomi_ST0_FT0(); + gen_op_fpop(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0x3e: /* fcomip */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_fmov_FT0_STN(opreg); + gen_op_fcomi_ST0_FT0(); + gen_op_fpop(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0x10 ... 0x13: /* fcmovxx */ + case 0x18 ... 0x1b: + { + int op1; + const static uint8_t fcmov_cc[8] = { + (JCC_B << 1), + (JCC_Z << 1), + (JCC_BE << 1), + (JCC_P << 1), + }; + op1 = fcmov_cc[op & 3] | ((op >> 3) & 1); + gen_setcc(s, op1); + gen_op_fcmov_ST0_STN_T0(opreg); + } + break; + default: + goto illegal_op; + } + } +#ifdef USE_CODE_COPY + s->tb->cflags |= CF_TB_FP_USED; +#endif + break; + /************************/ + /* string ops */ + + case 0xa4: /* movsS */ + case 0xa5: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_movs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); + } else { + gen_movs(s, ot); + } + break; + + case 0xaa: /* stosS */ + case 0xab: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_stos(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); + } else { + gen_stos(s, ot); + } + break; + case 0xac: /* lodsS */ + case 0xad: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_lods(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); + } else { + gen_lods(s, ot); + } + break; + case 0xae: /* scasS */ + case 0xaf: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + if (prefixes & PREFIX_REPNZ) { + gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1); + } else if (prefixes & PREFIX_REPZ) { + gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0); + } else { + gen_scas(s, ot); + s->cc_op = CC_OP_SUBB + ot; + } + break; + + case 0xa6: /* cmpsS */ + case 0xa7: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag + OT_WORD; + if (prefixes & PREFIX_REPNZ) { + gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1); + } else if (prefixes & PREFIX_REPZ) { + gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0); + } else { + gen_cmps(s, ot); + s->cc_op = CC_OP_SUBB + ot; + } + break; + case 0x6c: /* insS */ + case 0x6d: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + gen_check_io(s, ot, 1, pc_start - s->cs_base); + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_ins(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); + } else { + gen_ins(s, ot); + } + break; + case 0x6e: /* outsS */ + case 0x6f: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + gen_check_io(s, ot, 1, pc_start - s->cs_base); + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_outs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); + } else { + gen_outs(s, ot); + } + break; + + /************************/ + /* port I/O */ + case 0xe4: + case 0xe5: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + val = ldub_code(s->pc++); + gen_op_movl_T0_im(val); + gen_check_io(s, ot, 0, pc_start - s->cs_base); + gen_op_in[ot](); + gen_op_mov_reg_T1[ot][R_EAX](); + break; + case 0xe6: + case 0xe7: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + val = ldub_code(s->pc++); + gen_op_movl_T0_im(val); + gen_check_io(s, ot, 0, pc_start - s->cs_base); + gen_op_mov_TN_reg[ot][1][R_EAX](); + gen_op_out[ot](); + break; + case 0xec: + case 0xed: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + gen_op_mov_TN_reg[OT_WORD][0][R_EDX](); + gen_op_andl_T0_ffff(); + gen_check_io(s, ot, 0, pc_start - s->cs_base); + gen_op_in[ot](); + gen_op_mov_reg_T1[ot][R_EAX](); + break; + case 0xee: + case 0xef: + if ((b & 1) == 0) + ot = OT_BYTE; + else + ot = dflag ? OT_LONG : OT_WORD; + gen_op_mov_TN_reg[OT_WORD][0][R_EDX](); + gen_op_andl_T0_ffff(); + gen_check_io(s, ot, 0, pc_start - s->cs_base); + gen_op_mov_TN_reg[ot][1][R_EAX](); + gen_op_out[ot](); + break; + + /************************/ + /* control */ + case 0xc2: /* ret im */ + val = ldsw_code(s->pc); + s->pc += 2; + gen_pop_T0(s); + if (CODE64(s) && s->dflag) + s->dflag = 2; + gen_stack_update(s, val + (2 << s->dflag)); + if (s->dflag == 0) + gen_op_andl_T0_ffff(); + gen_op_jmp_T0(); + gen_eob(s); + break; + case 0xc3: /* ret */ + gen_pop_T0(s); + gen_pop_update(s); + if (s->dflag == 0) + gen_op_andl_T0_ffff(); + gen_op_jmp_T0(); + gen_eob(s); + break; + case 0xca: /* lret im */ + val = ldsw_code(s->pc); + s->pc += 2; + do_lret: + if (s->pe && !s->vm86) { + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_jmp_im(pc_start - s->cs_base); + gen_op_lret_protected(s->dflag, val); + } else { + gen_stack_A0(s); + /* pop offset */ + gen_op_ld_T0_A0[1 + s->dflag + s->mem_index](); + if (s->dflag == 0) + gen_op_andl_T0_ffff(); + /* NOTE: keeping EIP updated is not a problem in case of + exception */ + gen_op_jmp_T0(); + /* pop selector */ + gen_op_addl_A0_im(2 << s->dflag); + gen_op_ld_T0_A0[1 + s->dflag + s->mem_index](); + gen_op_movl_seg_T0_vm(offsetof(CPUX86State,segs[R_CS])); + /* add stack offset */ + gen_stack_update(s, val + (4 << s->dflag)); + } + gen_eob(s); + break; + case 0xcb: /* lret */ + val = 0; + goto do_lret; + case 0xcf: /* iret */ + if (!s->pe) { + /* real mode */ + gen_op_iret_real(s->dflag); + s->cc_op = CC_OP_EFLAGS; + } else if (s->vm86) { + if (s->iopl != 3) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_op_iret_real(s->dflag); + s->cc_op = CC_OP_EFLAGS; + } + } else { + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_jmp_im(pc_start - s->cs_base); + gen_op_iret_protected(s->dflag, s->pc - s->cs_base); + s->cc_op = CC_OP_EFLAGS; + } + gen_eob(s); + break; + case 0xe8: /* call im */ + { + if (dflag) + tval = (int32_t)insn_get(s, OT_LONG); + else + tval = (int16_t)insn_get(s, OT_WORD); + next_eip = s->pc - s->cs_base; + tval += next_eip; + if (s->dflag == 0) + tval &= 0xffff; + gen_movtl_T0_im(next_eip); + gen_push_T0(s); + gen_jmp(s, tval); + } + break; + case 0x9a: /* lcall im */ + { + unsigned int selector, offset; + + if (CODE64(s)) + goto illegal_op; + ot = dflag ? OT_LONG : OT_WORD; + offset = insn_get(s, ot); + selector = insn_get(s, OT_WORD); + + gen_op_movl_T0_im(selector); + gen_op_movl_T1_imu(offset); + } + goto do_lcall; + case 0xe9: /* jmp im */ + if (dflag) + tval = (int32_t)insn_get(s, OT_LONG); + else + tval = (int16_t)insn_get(s, OT_WORD); + tval += s->pc - s->cs_base; + if (s->dflag == 0) + tval &= 0xffff; + gen_jmp(s, tval); + break; + case 0xea: /* ljmp im */ + { + unsigned int selector, offset; + + if (CODE64(s)) + goto illegal_op; + ot = dflag ? OT_LONG : OT_WORD; + offset = insn_get(s, ot); + selector = insn_get(s, OT_WORD); + + gen_op_movl_T0_im(selector); + gen_op_movl_T1_imu(offset); + } + goto do_ljmp; + case 0xeb: /* jmp Jb */ + tval = (int8_t)insn_get(s, OT_BYTE); + tval += s->pc - s->cs_base; + if (s->dflag == 0) + tval &= 0xffff; + gen_jmp(s, tval); + break; + case 0x70 ... 0x7f: /* jcc Jb */ + tval = (int8_t)insn_get(s, OT_BYTE); + goto do_jcc; + case 0x180 ... 0x18f: /* jcc Jv */ + if (dflag) { + tval = (int32_t)insn_get(s, OT_LONG); + } else { + tval = (int16_t)insn_get(s, OT_WORD); + } + do_jcc: + next_eip = s->pc - s->cs_base; + tval += next_eip; + if (s->dflag == 0) + tval &= 0xffff; + gen_jcc(s, b, tval, next_eip); + break; + + case 0x190 ... 0x19f: /* setcc Gv */ + modrm = ldub_code(s->pc++); + gen_setcc(s, b); + gen_ldst_modrm(s, modrm, OT_BYTE, OR_TMP0, 1); + break; + case 0x140 ... 0x14f: /* cmov Gv, Ev */ + ot = dflag + OT_WORD; + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + gen_setcc(s, b); + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_ld_T1_A0[ot + s->mem_index](); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_mov_TN_reg[ot][1][rm](); + } + gen_op_cmov_reg_T1_T0[ot - OT_WORD][reg](); + break; + + /************************/ + /* flags */ + case 0x9c: /* pushf */ + if (s->vm86 && s->iopl != 3) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_movl_T0_eflags(); + gen_push_T0(s); + } + break; + case 0x9d: /* popf */ + if (s->vm86 && s->iopl != 3) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_pop_T0(s); + if (s->cpl == 0) { + if (s->dflag) { + gen_op_movl_eflags_T0_cpl0(); + } else { + gen_op_movw_eflags_T0_cpl0(); + } + } else { + if (s->cpl <= s->iopl) { + if (s->dflag) { + gen_op_movl_eflags_T0_io(); + } else { + gen_op_movw_eflags_T0_io(); + } + } else { + if (s->dflag) { + gen_op_movl_eflags_T0(); + } else { + gen_op_movw_eflags_T0(); + } + } + } + gen_pop_update(s); + s->cc_op = CC_OP_EFLAGS; + /* abort translation because TF flag may change */ + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + } + break; + case 0x9e: /* sahf */ + if (CODE64(s)) + goto illegal_op; + gen_op_mov_TN_reg[OT_BYTE][0][R_AH](); + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_movb_eflags_T0(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0x9f: /* lahf */ + if (CODE64(s)) + goto illegal_op; + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_movl_T0_eflags(); + gen_op_mov_reg_T0[OT_BYTE][R_AH](); + break; + case 0xf5: /* cmc */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_cmc(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0xf8: /* clc */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_clc(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0xf9: /* stc */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_stc(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0xfc: /* cld */ + gen_op_cld(); + break; + case 0xfd: /* std */ + gen_op_std(); + break; + + /************************/ + /* bit operations */ + case 0x1ba: /* bt/bts/btr/btc Gv, im */ + ot = dflag + OT_WORD; + modrm = ldub_code(s->pc++); + op = (modrm >> 3) & 7; + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + if (mod != 3) { + s->rip_offset = 1; + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_ld_T0_A0[ot + s->mem_index](); + } else { + gen_op_mov_TN_reg[ot][0][rm](); + } + /* load shift */ + val = ldub_code(s->pc++); + gen_op_movl_T1_im(val); + if (op < 4) + goto illegal_op; + op -= 4; + gen_op_btx_T0_T1_cc[ot - OT_WORD][op](); + s->cc_op = CC_OP_SARB + ot; + if (op != 0) { + if (mod != 3) + gen_op_st_T0_A0[ot + s->mem_index](); + else + gen_op_mov_reg_T0[ot][rm](); + gen_op_update_bt_cc(); + } + break; + case 0x1a3: /* bt Gv, Ev */ + op = 0; + goto do_btx; + case 0x1ab: /* bts */ + op = 1; + goto do_btx; + case 0x1b3: /* btr */ + op = 2; + goto do_btx; + case 0x1bb: /* btc */ + op = 3; + do_btx: + ot = dflag + OT_WORD; + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + gen_op_mov_TN_reg[OT_LONG][1][reg](); + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + /* specific case: we need to add a displacement */ + gen_op_add_bit_A0_T1[ot - OT_WORD](); + gen_op_ld_T0_A0[ot + s->mem_index](); + } else { + gen_op_mov_TN_reg[ot][0][rm](); + } + gen_op_btx_T0_T1_cc[ot - OT_WORD][op](); + s->cc_op = CC_OP_SARB + ot; + if (op != 0) { + if (mod != 3) + gen_op_st_T0_A0[ot + s->mem_index](); + else + gen_op_mov_reg_T0[ot][rm](); + gen_op_update_bt_cc(); + } + break; + case 0x1bc: /* bsf */ + case 0x1bd: /* bsr */ + ot = dflag + OT_WORD; + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); + /* NOTE: in order to handle the 0 case, we must load the + result. It could be optimized with a generated jump */ + gen_op_mov_TN_reg[ot][1][reg](); + gen_op_bsx_T0_cc[ot - OT_WORD][b & 1](); + gen_op_mov_reg_T1[ot][reg](); + s->cc_op = CC_OP_LOGICB + ot; + break; + /************************/ + /* bcd */ + case 0x27: /* daa */ + if (CODE64(s)) + goto illegal_op; + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_daa(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0x2f: /* das */ + if (CODE64(s)) + goto illegal_op; + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_das(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0x37: /* aaa */ + if (CODE64(s)) + goto illegal_op; + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_aaa(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0x3f: /* aas */ + if (CODE64(s)) + goto illegal_op; + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_aas(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0xd4: /* aam */ + if (CODE64(s)) + goto illegal_op; + val = ldub_code(s->pc++); + gen_op_aam(val); + s->cc_op = CC_OP_LOGICB; + break; + case 0xd5: /* aad */ + if (CODE64(s)) + goto illegal_op; + val = ldub_code(s->pc++); + gen_op_aad(val); + s->cc_op = CC_OP_LOGICB; + break; + /************************/ + /* misc */ + case 0x90: /* nop */ + /* XXX: xchg + rex handling */ + /* XXX: correct lock test for all insn */ + if (prefixes & PREFIX_LOCK) + goto illegal_op; + break; + case 0x9b: /* fwait */ + if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) == + (HF_MP_MASK | HF_TS_MASK)) { + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + } else { + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_jmp_im(pc_start - s->cs_base); + gen_op_fwait(); + } + break; + case 0xcc: /* int3 */ + gen_interrupt(s, EXCP03_INT3, pc_start - s->cs_base, s->pc - s->cs_base); + break; + case 0xcd: /* int N */ + val = ldub_code(s->pc++); + if (s->vm86 && s->iopl != 3) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_interrupt(s, val, pc_start - s->cs_base, s->pc - s->cs_base); + } + break; + case 0xce: /* into */ + if (CODE64(s)) + goto illegal_op; + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_jmp_im(pc_start - s->cs_base); + gen_op_into(s->pc - pc_start); + break; + case 0xf1: /* icebp (undocumented, exits to external debugger) */ +#if 1 + gen_debug(s, pc_start - s->cs_base); +#else + /* start debug */ + tb_flush(cpu_single_env); + cpu_set_log(CPU_LOG_INT | CPU_LOG_TB_IN_ASM); +#endif + break; + case 0xfa: /* cli */ + if (!s->vm86) { + if (s->cpl <= s->iopl) { + gen_op_cli(); + } else { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } + } else { + if (s->iopl == 3) { + gen_op_cli(); + } else { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } + } + break; + case 0xfb: /* sti */ + if (!s->vm86) { + if (s->cpl <= s->iopl) { + gen_sti: + gen_op_sti(); + /* interruptions are enabled only the first insn after sti */ + /* If several instructions disable interrupts, only the + _first_ does it */ + if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK)) + gen_op_set_inhibit_irq(); + /* give a chance to handle pending irqs */ + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + } else { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } + } else { + if (s->iopl == 3) { + goto gen_sti; + } else { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } + } + break; + case 0x62: /* bound */ + if (CODE64(s)) + goto illegal_op; + ot = dflag ? OT_LONG : OT_WORD; + modrm = ldub_code(s->pc++); + reg = (modrm >> 3) & 7; + mod = (modrm >> 6) & 3; + if (mod == 3) + goto illegal_op; + gen_op_mov_TN_reg[ot][0][reg](); + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_jmp_im(pc_start - s->cs_base); + if (ot == OT_WORD) + gen_op_boundw(); + else + gen_op_boundl(); + break; + case 0x1c8 ... 0x1cf: /* bswap reg */ + reg = (b & 7) | REX_B(s); +#ifdef TARGET_X86_64 + if (dflag == 2) { + gen_op_mov_TN_reg[OT_QUAD][0][reg](); + gen_op_bswapq_T0(); + gen_op_mov_reg_T0[OT_QUAD][reg](); + } else +#endif + { + gen_op_mov_TN_reg[OT_LONG][0][reg](); + gen_op_bswapl_T0(); + gen_op_mov_reg_T0[OT_LONG][reg](); + } + break; + case 0xd6: /* salc */ + if (CODE64(s)) + goto illegal_op; + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_salc(); + break; + case 0xe0: /* loopnz */ + case 0xe1: /* loopz */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + /* FALL THRU */ + case 0xe2: /* loop */ + case 0xe3: /* jecxz */ + { + int l1, l2; + + tval = (int8_t)insn_get(s, OT_BYTE); + next_eip = s->pc - s->cs_base; + tval += next_eip; + if (s->dflag == 0) + tval &= 0xffff; + + l1 = gen_new_label(); + l2 = gen_new_label(); + b &= 3; + if (b == 3) { + gen_op_jz_ecx[s->aflag](l1); + } else { + gen_op_dec_ECX[s->aflag](); + if (b <= 1) + gen_op_mov_T0_cc(); + gen_op_loop[s->aflag][b](l1); + } + + gen_jmp_im(next_eip); + gen_op_jmp_label(l2); + gen_set_label(l1); + gen_jmp_im(tval); + gen_set_label(l2); + gen_eob(s); + } + break; + case 0x130: /* wrmsr */ + case 0x132: /* rdmsr */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + if (b & 2) + gen_op_rdmsr(); + else + gen_op_wrmsr(); + } + break; + case 0x131: /* rdtsc */ + gen_jmp_im(pc_start - s->cs_base); + gen_op_rdtsc(); + break; + case 0x134: /* sysenter */ + if (CODE64(s)) + goto illegal_op; + if (!s->pe) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + if (s->cc_op != CC_OP_DYNAMIC) { + gen_op_set_cc_op(s->cc_op); + s->cc_op = CC_OP_DYNAMIC; + } + gen_jmp_im(pc_start - s->cs_base); + gen_op_sysenter(); + gen_eob(s); + } + break; + case 0x135: /* sysexit */ + if (CODE64(s)) + goto illegal_op; + if (!s->pe) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + if (s->cc_op != CC_OP_DYNAMIC) { + gen_op_set_cc_op(s->cc_op); + s->cc_op = CC_OP_DYNAMIC; + } + gen_jmp_im(pc_start - s->cs_base); + gen_op_sysexit(); + gen_eob(s); + } + break; +#ifdef TARGET_X86_64 + case 0x105: /* syscall */ + /* XXX: is it usable in real mode ? */ + if (s->cc_op != CC_OP_DYNAMIC) { + gen_op_set_cc_op(s->cc_op); + s->cc_op = CC_OP_DYNAMIC; + } + gen_jmp_im(pc_start - s->cs_base); + gen_op_syscall(s->pc - pc_start); + gen_eob(s); + break; + case 0x107: /* sysret */ + if (!s->pe) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + if (s->cc_op != CC_OP_DYNAMIC) { + gen_op_set_cc_op(s->cc_op); + s->cc_op = CC_OP_DYNAMIC; + } + gen_jmp_im(pc_start - s->cs_base); + gen_op_sysret(s->dflag); + /* condition codes are modified only in long mode */ + if (s->lma) + s->cc_op = CC_OP_EFLAGS; + gen_eob(s); + } + break; +#endif + case 0x1a2: /* cpuid */ + gen_op_cpuid(); + break; + case 0xf4: /* hlt */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_jmp_im(s->pc - s->cs_base); + gen_op_hlt(); + s->is_jmp = 3; + } + break; + case 0x100: + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + op = (modrm >> 3) & 7; + switch(op) { + case 0: /* sldt */ + if (!s->pe || s->vm86) + goto illegal_op; + gen_op_movl_T0_env(offsetof(CPUX86State,ldt.selector)); + ot = OT_WORD; + if (mod == 3) + ot += s->dflag; + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1); + break; + case 2: /* lldt */ + if (!s->pe || s->vm86) + goto illegal_op; + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0); + gen_jmp_im(pc_start - s->cs_base); + gen_op_lldt_T0(); + } + break; + case 1: /* str */ + if (!s->pe || s->vm86) + goto illegal_op; + gen_op_movl_T0_env(offsetof(CPUX86State,tr.selector)); + ot = OT_WORD; + if (mod == 3) + ot += s->dflag; + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1); + break; + case 3: /* ltr */ + if (!s->pe || s->vm86) + goto illegal_op; + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0); + gen_jmp_im(pc_start - s->cs_base); + gen_op_ltr_T0(); + } + break; + case 4: /* verr */ + case 5: /* verw */ + if (!s->pe || s->vm86) + goto illegal_op; + gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0); + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + if (op == 4) + gen_op_verr(); + else + gen_op_verw(); + s->cc_op = CC_OP_EFLAGS; + break; + default: + goto illegal_op; + } + break; + case 0x101: + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + op = (modrm >> 3) & 7; + switch(op) { + case 0: /* sgdt */ + case 1: /* sidt */ + if (mod == 3) + goto illegal_op; + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + if (op == 0) + gen_op_movl_T0_env(offsetof(CPUX86State,gdt.limit)); + else + gen_op_movl_T0_env(offsetof(CPUX86State,idt.limit)); + gen_op_st_T0_A0[OT_WORD + s->mem_index](); + gen_add_A0_im(s, 2); + if (op == 0) + gen_op_movtl_T0_env(offsetof(CPUX86State,gdt.base)); + else + gen_op_movtl_T0_env(offsetof(CPUX86State,idt.base)); + if (!s->dflag) + gen_op_andl_T0_im(0xffffff); + gen_op_st_T0_A0[CODE64(s) + OT_LONG + s->mem_index](); + break; + case 2: /* lgdt */ + case 3: /* lidt */ + if (mod == 3) + goto illegal_op; + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_ld_T1_A0[OT_WORD + s->mem_index](); + gen_add_A0_im(s, 2); + gen_op_ld_T0_A0[CODE64(s) + OT_LONG + s->mem_index](); + if (!s->dflag) + gen_op_andl_T0_im(0xffffff); + if (op == 2) { + gen_op_movtl_env_T0(offsetof(CPUX86State,gdt.base)); + gen_op_movl_env_T1(offsetof(CPUX86State,gdt.limit)); + } else { + gen_op_movtl_env_T0(offsetof(CPUX86State,idt.base)); + gen_op_movl_env_T1(offsetof(CPUX86State,idt.limit)); + } + } + break; + case 4: /* smsw */ + gen_op_movl_T0_env(offsetof(CPUX86State,cr[0])); + gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 1); + break; + case 6: /* lmsw */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0); + gen_op_lmsw_T0(); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + } + break; + case 7: /* invlpg */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + if (mod == 3) { +#ifdef TARGET_X86_64 + if (CODE64(s) && (modrm & 7) == 0) { + /* swapgs */ + gen_op_movtl_T0_env(offsetof(CPUX86State,segs[R_GS].base)); + gen_op_movtl_T1_env(offsetof(CPUX86State,kernelgsbase)); + gen_op_movtl_env_T1(offsetof(CPUX86State,segs[R_GS].base)); + gen_op_movtl_env_T0(offsetof(CPUX86State,kernelgsbase)); + } else +#endif + { + goto illegal_op; + } + } else { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_invlpg_A0(); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + } + } + break; + default: + goto illegal_op; + } + break; + case 0x108: /* invd */ + case 0x109: /* wbinvd */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + /* nothing to do */ + } + break; + case 0x63: /* arpl or movslS (x86_64) */ +#ifdef TARGET_X86_64 + if (CODE64(s)) { + int d_ot; + /* d_ot is the size of destination */ + d_ot = dflag + OT_WORD; + + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + + if (mod == 3) { + gen_op_mov_TN_reg[OT_LONG][0][rm](); + /* sign extend */ + if (d_ot == OT_QUAD) + gen_op_movslq_T0_T0(); + gen_op_mov_reg_T0[d_ot][reg](); + } else { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + if (d_ot == OT_QUAD) { + gen_op_lds_T0_A0[OT_LONG + s->mem_index](); + } else { + gen_op_ld_T0_A0[OT_LONG + s->mem_index](); + } + gen_op_mov_reg_T0[d_ot][reg](); + } + } else +#endif + { + if (!s->pe || s->vm86) + goto illegal_op; + ot = dflag ? OT_LONG : OT_WORD; + modrm = ldub_code(s->pc++); + reg = (modrm >> 3) & 7; + mod = (modrm >> 6) & 3; + rm = modrm & 7; + if (mod != 3) { + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_ld_T0_A0[ot + s->mem_index](); + } else { + gen_op_mov_TN_reg[ot][0][rm](); + } + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_arpl(); + s->cc_op = CC_OP_EFLAGS; + if (mod != 3) { + gen_op_st_T0_A0[ot + s->mem_index](); + } else { + gen_op_mov_reg_T0[ot][rm](); + } + gen_op_arpl_update(); + } + break; + case 0x102: /* lar */ + case 0x103: /* lsl */ + if (!s->pe || s->vm86) + goto illegal_op; + ot = dflag ? OT_LONG : OT_WORD; + modrm = ldub_code(s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); + gen_op_mov_TN_reg[ot][1][reg](); + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + if (b == 0x102) + gen_op_lar(); + else + gen_op_lsl(); + s->cc_op = CC_OP_EFLAGS; + gen_op_mov_reg_T1[ot][reg](); + break; + case 0x118: + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + op = (modrm >> 3) & 7; + switch(op) { + case 0: /* prefetchnta */ + case 1: /* prefetchnt0 */ + case 2: /* prefetchnt0 */ + case 3: /* prefetchnt0 */ + if (mod == 3) + goto illegal_op; + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + /* nothing more to do */ + break; + default: + goto illegal_op; + } + break; + case 0x120: /* mov reg, crN */ + case 0x122: /* mov crN, reg */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + modrm = ldub_code(s->pc++); + if ((modrm & 0xc0) != 0xc0) + goto illegal_op; + rm = (modrm & 7) | REX_B(s); + reg = ((modrm >> 3) & 7) | rex_r; + if (CODE64(s)) + ot = OT_QUAD; + else + ot = OT_LONG; + switch(reg) { + case 0: + case 2: + case 3: + case 4: + case 8: + if (b & 2) { + gen_op_mov_TN_reg[ot][0][rm](); + gen_op_movl_crN_T0(reg); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + } else { +#if !defined(CONFIG_USER_ONLY) + if (reg == 8) + gen_op_movtl_T0_cr8(); + else +#endif + gen_op_movtl_T0_env(offsetof(CPUX86State,cr[reg])); + gen_op_mov_reg_T0[ot][rm](); + } + break; + default: + goto illegal_op; + } + } + break; + case 0x121: /* mov reg, drN */ + case 0x123: /* mov drN, reg */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + modrm = ldub_code(s->pc++); + if ((modrm & 0xc0) != 0xc0) + goto illegal_op; + rm = (modrm & 7) | REX_B(s); + reg = ((modrm >> 3) & 7) | rex_r; + if (CODE64(s)) + ot = OT_QUAD; + else + ot = OT_LONG; + /* XXX: do it dynamically with CR4.DE bit */ + if (reg == 4 || reg == 5 || reg >= 8) + goto illegal_op; + if (b & 2) { + gen_op_mov_TN_reg[ot][0][rm](); + gen_op_movl_drN_T0(reg); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + } else { + gen_op_movtl_T0_env(offsetof(CPUX86State,dr[reg])); + gen_op_mov_reg_T0[ot][rm](); + } + } + break; + case 0x106: /* clts */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_op_clts(); + /* abort block because static cpu state changed */ + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + } + break; + /* MMX/SSE/SSE2/PNI support */ + case 0x1c3: /* MOVNTI reg, mem */ + if (!(s->cpuid_features & CPUID_SSE2)) + goto illegal_op; + ot = s->dflag == 2 ? OT_QUAD : OT_LONG; + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + if (mod == 3) + goto illegal_op; + reg = ((modrm >> 3) & 7) | rex_r; + /* generate a generic store */ + gen_ldst_modrm(s, modrm, ot, reg, 1); + break; + case 0x1ae: + modrm = ldub_code(s->pc++); + mod = (modrm >> 6) & 3; + op = (modrm >> 3) & 7; + switch(op) { + case 0: /* fxsave */ + if (mod == 3 || !(s->cpuid_features & CPUID_FXSR) || + (s->flags & HF_EM_MASK)) + goto illegal_op; + if (s->flags & HF_TS_MASK) { + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + break; + } + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_fxsave_A0((s->dflag == 2)); + break; + case 1: /* fxrstor */ + if (mod == 3 || !(s->cpuid_features & CPUID_FXSR) || + (s->flags & HF_EM_MASK)) + goto illegal_op; + if (s->flags & HF_TS_MASK) { + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + break; + } + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + gen_op_fxrstor_A0((s->dflag == 2)); + break; + case 2: /* ldmxcsr */ + case 3: /* stmxcsr */ + if (s->flags & HF_TS_MASK) { + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + break; + } + if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK) || + mod == 3) + goto illegal_op; + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + if (op == 2) { + gen_op_ld_T0_A0[OT_LONG + s->mem_index](); + gen_op_movl_env_T0(offsetof(CPUX86State, mxcsr)); + } else { + gen_op_movl_T0_env(offsetof(CPUX86State, mxcsr)); + gen_op_st_T0_A0[OT_LONG + s->mem_index](); + } + break; + case 5: /* lfence */ + case 6: /* mfence */ + if ((modrm & 0xc7) != 0xc0 || !(s->cpuid_features & CPUID_SSE)) + goto illegal_op; + break; + case 7: /* sfence / clflush */ + if ((modrm & 0xc7) == 0xc0) { + /* sfence */ + if (!(s->cpuid_features & CPUID_SSE)) + goto illegal_op; + } else { + /* clflush */ + if (!(s->cpuid_features & CPUID_CLFLUSH)) + goto illegal_op; + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + } + break; + default: + goto illegal_op; + } + break; + case 0x10d: /* prefetch */ + modrm = ldub_code(s->pc++); + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); + /* ignore for now */ + break; + case 0x110 ... 0x117: + case 0x128 ... 0x12f: + case 0x150 ... 0x177: + case 0x17c ... 0x17f: + case 0x1c2: + case 0x1c4 ... 0x1c6: + case 0x1d0 ... 0x1fe: + gen_sse(s, b, pc_start, rex_r); + break; + default: + goto illegal_op; + } + /* lock generation */ + if (s->prefix & PREFIX_LOCK) + gen_op_unlock(); + return s->pc; + illegal_op: + if (s->prefix & PREFIX_LOCK) + gen_op_unlock(); + /* XXX: ensure that no lock was generated */ + gen_exception(s, EXCP06_ILLOP, pc_start - s->cs_base); + return s->pc; +} + +#define CC_OSZAPC (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C) +#define CC_OSZAP (CC_O | CC_S | CC_Z | CC_A | CC_P) + +/* flags read by an operation */ +static uint16_t opc_read_flags[NB_OPS] = { + [INDEX_op_aas] = CC_A, + [INDEX_op_aaa] = CC_A, + [INDEX_op_das] = CC_A | CC_C, + [INDEX_op_daa] = CC_A | CC_C, + + /* subtle: due to the incl/decl implementation, C is used */ + [INDEX_op_update_inc_cc] = CC_C, + + [INDEX_op_into] = CC_O, + + [INDEX_op_jb_subb] = CC_C, + [INDEX_op_jb_subw] = CC_C, + [INDEX_op_jb_subl] = CC_C, + + [INDEX_op_jz_subb] = CC_Z, + [INDEX_op_jz_subw] = CC_Z, + [INDEX_op_jz_subl] = CC_Z, + + [INDEX_op_jbe_subb] = CC_Z | CC_C, + [INDEX_op_jbe_subw] = CC_Z | CC_C, + [INDEX_op_jbe_subl] = CC_Z | CC_C, + + [INDEX_op_js_subb] = CC_S, + [INDEX_op_js_subw] = CC_S, + [INDEX_op_js_subl] = CC_S, + + [INDEX_op_jl_subb] = CC_O | CC_S, + [INDEX_op_jl_subw] = CC_O | CC_S, + [INDEX_op_jl_subl] = CC_O | CC_S, + + [INDEX_op_jle_subb] = CC_O | CC_S | CC_Z, + [INDEX_op_jle_subw] = CC_O | CC_S | CC_Z, + [INDEX_op_jle_subl] = CC_O | CC_S | CC_Z, + + [INDEX_op_loopnzw] = CC_Z, + [INDEX_op_loopnzl] = CC_Z, + [INDEX_op_loopzw] = CC_Z, + [INDEX_op_loopzl] = CC_Z, + + [INDEX_op_seto_T0_cc] = CC_O, + [INDEX_op_setb_T0_cc] = CC_C, + [INDEX_op_setz_T0_cc] = CC_Z, + [INDEX_op_setbe_T0_cc] = CC_Z | CC_C, + [INDEX_op_sets_T0_cc] = CC_S, + [INDEX_op_setp_T0_cc] = CC_P, + [INDEX_op_setl_T0_cc] = CC_O | CC_S, + [INDEX_op_setle_T0_cc] = CC_O | CC_S | CC_Z, + + [INDEX_op_setb_T0_subb] = CC_C, + [INDEX_op_setb_T0_subw] = CC_C, + [INDEX_op_setb_T0_subl] = CC_C, + + [INDEX_op_setz_T0_subb] = CC_Z, + [INDEX_op_setz_T0_subw] = CC_Z, + [INDEX_op_setz_T0_subl] = CC_Z, + + [INDEX_op_setbe_T0_subb] = CC_Z | CC_C, + [INDEX_op_setbe_T0_subw] = CC_Z | CC_C, + [INDEX_op_setbe_T0_subl] = CC_Z | CC_C, + + [INDEX_op_sets_T0_subb] = CC_S, + [INDEX_op_sets_T0_subw] = CC_S, + [INDEX_op_sets_T0_subl] = CC_S, + + [INDEX_op_setl_T0_subb] = CC_O | CC_S, + [INDEX_op_setl_T0_subw] = CC_O | CC_S, + [INDEX_op_setl_T0_subl] = CC_O | CC_S, + + [INDEX_op_setle_T0_subb] = CC_O | CC_S | CC_Z, + [INDEX_op_setle_T0_subw] = CC_O | CC_S | CC_Z, + [INDEX_op_setle_T0_subl] = CC_O | CC_S | CC_Z, + + [INDEX_op_movl_T0_eflags] = CC_OSZAPC, + [INDEX_op_cmc] = CC_C, + [INDEX_op_salc] = CC_C, + + /* needed for correct flag optimisation before string ops */ + [INDEX_op_jnz_ecxw] = CC_OSZAPC, + [INDEX_op_jnz_ecxl] = CC_OSZAPC, + [INDEX_op_jz_ecxw] = CC_OSZAPC, + [INDEX_op_jz_ecxl] = CC_OSZAPC, + +#ifdef TARGET_X86_64 + [INDEX_op_jb_subq] = CC_C, + [INDEX_op_jz_subq] = CC_Z, + [INDEX_op_jbe_subq] = CC_Z | CC_C, + [INDEX_op_js_subq] = CC_S, + [INDEX_op_jl_subq] = CC_O | CC_S, + [INDEX_op_jle_subq] = CC_O | CC_S | CC_Z, + + [INDEX_op_loopnzq] = CC_Z, + [INDEX_op_loopzq] = CC_Z, + + [INDEX_op_setb_T0_subq] = CC_C, + [INDEX_op_setz_T0_subq] = CC_Z, + [INDEX_op_setbe_T0_subq] = CC_Z | CC_C, + [INDEX_op_sets_T0_subq] = CC_S, + [INDEX_op_setl_T0_subq] = CC_O | CC_S, + [INDEX_op_setle_T0_subq] = CC_O | CC_S | CC_Z, + + [INDEX_op_jnz_ecxq] = CC_OSZAPC, + [INDEX_op_jz_ecxq] = CC_OSZAPC, +#endif + +#define DEF_READF(SUFFIX)\ + [INDEX_op_adcb ## SUFFIX ## _T0_T1_cc] = CC_C,\ + [INDEX_op_adcw ## SUFFIX ## _T0_T1_cc] = CC_C,\ + [INDEX_op_adcl ## SUFFIX ## _T0_T1_cc] = CC_C,\ + X86_64_DEF([INDEX_op_adcq ## SUFFIX ## _T0_T1_cc] = CC_C,)\ + [INDEX_op_sbbb ## SUFFIX ## _T0_T1_cc] = CC_C,\ + [INDEX_op_sbbw ## SUFFIX ## _T0_T1_cc] = CC_C,\ + [INDEX_op_sbbl ## SUFFIX ## _T0_T1_cc] = CC_C,\ + X86_64_DEF([INDEX_op_sbbq ## SUFFIX ## _T0_T1_cc] = CC_C,)\ +\ + [INDEX_op_rclb ## SUFFIX ## _T0_T1_cc] = CC_C,\ + [INDEX_op_rclw ## SUFFIX ## _T0_T1_cc] = CC_C,\ + [INDEX_op_rcll ## SUFFIX ## _T0_T1_cc] = CC_C,\ + X86_64_DEF([INDEX_op_rclq ## SUFFIX ## _T0_T1_cc] = CC_C,)\ + [INDEX_op_rcrb ## SUFFIX ## _T0_T1_cc] = CC_C,\ + [INDEX_op_rcrw ## SUFFIX ## _T0_T1_cc] = CC_C,\ + [INDEX_op_rcrl ## SUFFIX ## _T0_T1_cc] = CC_C,\ + X86_64_DEF([INDEX_op_rcrq ## SUFFIX ## _T0_T1_cc] = CC_C,) + + DEF_READF( ) + DEF_READF(_raw) +#ifndef CONFIG_USER_ONLY + DEF_READF(_kernel) + DEF_READF(_user) +#endif +}; + +/* flags written by an operation */ +static uint16_t opc_write_flags[NB_OPS] = { + [INDEX_op_update2_cc] = CC_OSZAPC, + [INDEX_op_update1_cc] = CC_OSZAPC, + [INDEX_op_cmpl_T0_T1_cc] = CC_OSZAPC, + [INDEX_op_update_neg_cc] = CC_OSZAPC, + /* subtle: due to the incl/decl implementation, C is used */ + [INDEX_op_update_inc_cc] = CC_OSZAPC, + [INDEX_op_testl_T0_T1_cc] = CC_OSZAPC, + + [INDEX_op_mulb_AL_T0] = CC_OSZAPC, + [INDEX_op_mulw_AX_T0] = CC_OSZAPC, + [INDEX_op_mull_EAX_T0] = CC_OSZAPC, + X86_64_DEF([INDEX_op_mulq_EAX_T0] = CC_OSZAPC,) + [INDEX_op_imulb_AL_T0] = CC_OSZAPC, + [INDEX_op_imulw_AX_T0] = CC_OSZAPC, + [INDEX_op_imull_EAX_T0] = CC_OSZAPC, + X86_64_DEF([INDEX_op_imulq_EAX_T0] = CC_OSZAPC,) + [INDEX_op_imulw_T0_T1] = CC_OSZAPC, + [INDEX_op_imull_T0_T1] = CC_OSZAPC, + X86_64_DEF([INDEX_op_imulq_T0_T1] = CC_OSZAPC,) + + /* sse */ + [INDEX_op_ucomiss] = CC_OSZAPC, + [INDEX_op_ucomisd] = CC_OSZAPC, + [INDEX_op_comiss] = CC_OSZAPC, + [INDEX_op_comisd] = CC_OSZAPC, + + /* bcd */ + [INDEX_op_aam] = CC_OSZAPC, + [INDEX_op_aad] = CC_OSZAPC, + [INDEX_op_aas] = CC_OSZAPC, + [INDEX_op_aaa] = CC_OSZAPC, + [INDEX_op_das] = CC_OSZAPC, + [INDEX_op_daa] = CC_OSZAPC, + + [INDEX_op_movb_eflags_T0] = CC_S | CC_Z | CC_A | CC_P | CC_C, + [INDEX_op_movw_eflags_T0] = CC_OSZAPC, + [INDEX_op_movl_eflags_T0] = CC_OSZAPC, + [INDEX_op_movw_eflags_T0_io] = CC_OSZAPC, + [INDEX_op_movl_eflags_T0_io] = CC_OSZAPC, + [INDEX_op_movw_eflags_T0_cpl0] = CC_OSZAPC, + [INDEX_op_movl_eflags_T0_cpl0] = CC_OSZAPC, + [INDEX_op_clc] = CC_C, + [INDEX_op_stc] = CC_C, + [INDEX_op_cmc] = CC_C, + + [INDEX_op_btw_T0_T1_cc] = CC_OSZAPC, + [INDEX_op_btl_T0_T1_cc] = CC_OSZAPC, + X86_64_DEF([INDEX_op_btq_T0_T1_cc] = CC_OSZAPC,) + [INDEX_op_btsw_T0_T1_cc] = CC_OSZAPC, + [INDEX_op_btsl_T0_T1_cc] = CC_OSZAPC, + X86_64_DEF([INDEX_op_btsq_T0_T1_cc] = CC_OSZAPC,) + [INDEX_op_btrw_T0_T1_cc] = CC_OSZAPC, + [INDEX_op_btrl_T0_T1_cc] = CC_OSZAPC, + X86_64_DEF([INDEX_op_btrq_T0_T1_cc] = CC_OSZAPC,) + [INDEX_op_btcw_T0_T1_cc] = CC_OSZAPC, + [INDEX_op_btcl_T0_T1_cc] = CC_OSZAPC, + X86_64_DEF([INDEX_op_btcq_T0_T1_cc] = CC_OSZAPC,) + + [INDEX_op_bsfw_T0_cc] = CC_OSZAPC, + [INDEX_op_bsfl_T0_cc] = CC_OSZAPC, + X86_64_DEF([INDEX_op_bsfq_T0_cc] = CC_OSZAPC,) + [INDEX_op_bsrw_T0_cc] = CC_OSZAPC, + [INDEX_op_bsrl_T0_cc] = CC_OSZAPC, + X86_64_DEF([INDEX_op_bsrq_T0_cc] = CC_OSZAPC,) + + [INDEX_op_cmpxchgb_T0_T1_EAX_cc] = CC_OSZAPC, + [INDEX_op_cmpxchgw_T0_T1_EAX_cc] = CC_OSZAPC, + [INDEX_op_cmpxchgl_T0_T1_EAX_cc] = CC_OSZAPC, + X86_64_DEF([INDEX_op_cmpxchgq_T0_T1_EAX_cc] = CC_OSZAPC,) + + [INDEX_op_cmpxchg8b] = CC_Z, + [INDEX_op_lar] = CC_Z, + [INDEX_op_lsl] = CC_Z, + [INDEX_op_verr] = CC_Z, + [INDEX_op_verw] = CC_Z, + [INDEX_op_fcomi_ST0_FT0] = CC_Z | CC_P | CC_C, + [INDEX_op_fucomi_ST0_FT0] = CC_Z | CC_P | CC_C, + +#define DEF_WRITEF(SUFFIX)\ + [INDEX_op_adcb ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + [INDEX_op_adcw ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + [INDEX_op_adcl ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + X86_64_DEF([INDEX_op_adcq ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,)\ + [INDEX_op_sbbb ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + [INDEX_op_sbbw ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + [INDEX_op_sbbl ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + X86_64_DEF([INDEX_op_sbbq ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,)\ +\ + [INDEX_op_rolb ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\ + [INDEX_op_rolw ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\ + [INDEX_op_roll ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\ + X86_64_DEF([INDEX_op_rolq ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,)\ + [INDEX_op_rorb ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\ + [INDEX_op_rorw ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\ + [INDEX_op_rorl ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\ + X86_64_DEF([INDEX_op_rorq ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,)\ +\ + [INDEX_op_rclb ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\ + [INDEX_op_rclw ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\ + [INDEX_op_rcll ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\ + X86_64_DEF([INDEX_op_rclq ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,)\ + [INDEX_op_rcrb ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\ + [INDEX_op_rcrw ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\ + [INDEX_op_rcrl ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,\ + X86_64_DEF([INDEX_op_rcrq ## SUFFIX ## _T0_T1_cc] = CC_O | CC_C,)\ +\ + [INDEX_op_shlb ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + [INDEX_op_shlw ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + [INDEX_op_shll ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + X86_64_DEF([INDEX_op_shlq ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,)\ +\ + [INDEX_op_shrb ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + [INDEX_op_shrw ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + [INDEX_op_shrl ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + X86_64_DEF([INDEX_op_shrq ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,)\ +\ + [INDEX_op_sarb ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + [INDEX_op_sarw ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + [INDEX_op_sarl ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,\ + X86_64_DEF([INDEX_op_sarq ## SUFFIX ## _T0_T1_cc] = CC_OSZAPC,)\ +\ + [INDEX_op_shldw ## SUFFIX ## _T0_T1_ECX_cc] = CC_OSZAPC,\ + [INDEX_op_shldl ## SUFFIX ## _T0_T1_ECX_cc] = CC_OSZAPC,\ + X86_64_DEF([INDEX_op_shldq ## SUFFIX ## _T0_T1_ECX_cc] = CC_OSZAPC,)\ + [INDEX_op_shldw ## SUFFIX ## _T0_T1_im_cc] = CC_OSZAPC,\ + [INDEX_op_shldl ## SUFFIX ## _T0_T1_im_cc] = CC_OSZAPC,\ + X86_64_DEF([INDEX_op_shldq ## SUFFIX ## _T0_T1_im_cc] = CC_OSZAPC,)\ +\ + [INDEX_op_shrdw ## SUFFIX ## _T0_T1_ECX_cc] = CC_OSZAPC,\ + [INDEX_op_shrdl ## SUFFIX ## _T0_T1_ECX_cc] = CC_OSZAPC,\ + X86_64_DEF([INDEX_op_shrdq ## SUFFIX ## _T0_T1_ECX_cc] = CC_OSZAPC,)\ + [INDEX_op_shrdw ## SUFFIX ## _T0_T1_im_cc] = CC_OSZAPC,\ + [INDEX_op_shrdl ## SUFFIX ## _T0_T1_im_cc] = CC_OSZAPC,\ + X86_64_DEF([INDEX_op_shrdq ## SUFFIX ## _T0_T1_im_cc] = CC_OSZAPC,)\ +\ + [INDEX_op_cmpxchgb ## SUFFIX ## _T0_T1_EAX_cc] = CC_OSZAPC,\ + [INDEX_op_cmpxchgw ## SUFFIX ## _T0_T1_EAX_cc] = CC_OSZAPC,\ + [INDEX_op_cmpxchgl ## SUFFIX ## _T0_T1_EAX_cc] = CC_OSZAPC,\ + X86_64_DEF([INDEX_op_cmpxchgq ## SUFFIX ## _T0_T1_EAX_cc] = CC_OSZAPC,) + + + DEF_WRITEF( ) + DEF_WRITEF(_raw) +#ifndef CONFIG_USER_ONLY + DEF_WRITEF(_kernel) + DEF_WRITEF(_user) +#endif +}; + +/* simpler form of an operation if no flags need to be generated */ +static uint16_t opc_simpler[NB_OPS] = { + [INDEX_op_update2_cc] = INDEX_op_nop, + [INDEX_op_update1_cc] = INDEX_op_nop, + [INDEX_op_update_neg_cc] = INDEX_op_nop, +#if 0 + /* broken: CC_OP logic must be rewritten */ + [INDEX_op_update_inc_cc] = INDEX_op_nop, +#endif + + [INDEX_op_shlb_T0_T1_cc] = INDEX_op_shlb_T0_T1, + [INDEX_op_shlw_T0_T1_cc] = INDEX_op_shlw_T0_T1, + [INDEX_op_shll_T0_T1_cc] = INDEX_op_shll_T0_T1, + X86_64_DEF([INDEX_op_shlq_T0_T1_cc] = INDEX_op_shlq_T0_T1,) + + [INDEX_op_shrb_T0_T1_cc] = INDEX_op_shrb_T0_T1, + [INDEX_op_shrw_T0_T1_cc] = INDEX_op_shrw_T0_T1, + [INDEX_op_shrl_T0_T1_cc] = INDEX_op_shrl_T0_T1, + X86_64_DEF([INDEX_op_shrq_T0_T1_cc] = INDEX_op_shrq_T0_T1,) + + [INDEX_op_sarb_T0_T1_cc] = INDEX_op_sarb_T0_T1, + [INDEX_op_sarw_T0_T1_cc] = INDEX_op_sarw_T0_T1, + [INDEX_op_sarl_T0_T1_cc] = INDEX_op_sarl_T0_T1, + X86_64_DEF([INDEX_op_sarq_T0_T1_cc] = INDEX_op_sarq_T0_T1,) + +#define DEF_SIMPLER(SUFFIX)\ + [INDEX_op_rolb ## SUFFIX ## _T0_T1_cc] = INDEX_op_rolb ## SUFFIX ## _T0_T1,\ + [INDEX_op_rolw ## SUFFIX ## _T0_T1_cc] = INDEX_op_rolw ## SUFFIX ## _T0_T1,\ + [INDEX_op_roll ## SUFFIX ## _T0_T1_cc] = INDEX_op_roll ## SUFFIX ## _T0_T1,\ + X86_64_DEF([INDEX_op_rolq ## SUFFIX ## _T0_T1_cc] = INDEX_op_rolq ## SUFFIX ## _T0_T1,)\ +\ + [INDEX_op_rorb ## SUFFIX ## _T0_T1_cc] = INDEX_op_rorb ## SUFFIX ## _T0_T1,\ + [INDEX_op_rorw ## SUFFIX ## _T0_T1_cc] = INDEX_op_rorw ## SUFFIX ## _T0_T1,\ + [INDEX_op_rorl ## SUFFIX ## _T0_T1_cc] = INDEX_op_rorl ## SUFFIX ## _T0_T1,\ + X86_64_DEF([INDEX_op_rorq ## SUFFIX ## _T0_T1_cc] = INDEX_op_rorq ## SUFFIX ## _T0_T1,) + + DEF_SIMPLER( ) + DEF_SIMPLER(_raw) +#ifndef CONFIG_USER_ONLY + DEF_SIMPLER(_kernel) + DEF_SIMPLER(_user) +#endif +}; + +void optimize_flags_init(void) +{ + int i; + /* put default values in arrays */ + for(i = 0; i < NB_OPS; i++) { + if (opc_simpler[i] == 0) + opc_simpler[i] = i; + } +} + +/* CPU flags computation optimization: we move backward thru the + generated code to see which flags are needed. The operation is + modified if suitable */ +static void optimize_flags(uint16_t *opc_buf, int opc_buf_len) +{ + uint16_t *opc_ptr; + int live_flags, write_flags, op; + + opc_ptr = opc_buf + opc_buf_len; + /* live_flags contains the flags needed by the next instructions + in the code. At the end of the bloc, we consider that all the + flags are live. */ + live_flags = CC_OSZAPC; + while (opc_ptr > opc_buf) { + op = *--opc_ptr; + /* if none of the flags written by the instruction is used, + then we can try to find a simpler instruction */ + write_flags = opc_write_flags[op]; + if ((live_flags & write_flags) == 0) { + *opc_ptr = opc_simpler[op]; + } + /* compute the live flags before the instruction */ + live_flags &= ~write_flags; + live_flags |= opc_read_flags[op]; + } +} + +/* generate intermediate code in gen_opc_buf and gen_opparam_buf for + basic block 'tb'. If search_pc is TRUE, also generate PC + information for each intermediate instruction. */ +static inline int gen_intermediate_code_internal(CPUState *env, + TranslationBlock *tb, + int search_pc) +{ + DisasContext dc1, *dc = &dc1; + target_ulong pc_ptr; + uint16_t *gen_opc_end; + int flags, j, lj, cflags; + target_ulong pc_start; + target_ulong cs_base; + + /* generate intermediate code */ + pc_start = tb->pc; + cs_base = tb->cs_base; + flags = tb->flags; + cflags = tb->cflags; + + dc->pe = (flags >> HF_PE_SHIFT) & 1; + dc->code32 = (flags >> HF_CS32_SHIFT) & 1; + dc->ss32 = (flags >> HF_SS32_SHIFT) & 1; + dc->addseg = (flags >> HF_ADDSEG_SHIFT) & 1; + dc->f_st = 0; + dc->vm86 = (flags >> VM_SHIFT) & 1; + dc->cpl = (flags >> HF_CPL_SHIFT) & 3; + dc->iopl = (flags >> IOPL_SHIFT) & 3; + dc->tf = (flags >> TF_SHIFT) & 1; + dc->singlestep_enabled = env->singlestep_enabled; + dc->cc_op = CC_OP_DYNAMIC; + dc->cs_base = cs_base; + dc->tb = tb; + dc->popl_esp_hack = 0; + /* select memory access functions */ + dc->mem_index = 0; + if (flags & HF_SOFTMMU_MASK) { + if (dc->cpl == 3) + dc->mem_index = 2 * 4; + else + dc->mem_index = 1 * 4; + } + dc->cpuid_features = env->cpuid_features; +#ifdef TARGET_X86_64 + dc->lma = (flags >> HF_LMA_SHIFT) & 1; + dc->code64 = (flags >> HF_CS64_SHIFT) & 1; +#endif + dc->flags = flags; + dc->jmp_opt = !(dc->tf || env->singlestep_enabled || + (flags & HF_INHIBIT_IRQ_MASK) +#ifndef CONFIG_SOFTMMU + || (flags & HF_SOFTMMU_MASK) +#endif + ); +#if 0 + /* check addseg logic */ + if (!dc->addseg && (dc->vm86 || !dc->pe || !dc->code32)) + printf("ERROR addseg\n"); +#endif + + gen_opc_ptr = gen_opc_buf; + gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; + gen_opparam_ptr = gen_opparam_buf; + nb_gen_labels = 0; + + dc->is_jmp = DISAS_NEXT; + pc_ptr = pc_start; + lj = -1; + + for(;;) { + if (env->nb_breakpoints > 0) { + for(j = 0; j < env->nb_breakpoints; j++) { + if (env->breakpoints[j] == pc_ptr) { + gen_debug(dc, pc_ptr - dc->cs_base); + break; + } + } + } + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + if (lj < j) { + lj++; + while (lj < j) + gen_opc_instr_start[lj++] = 0; + } + gen_opc_pc[lj] = pc_ptr; + gen_opc_cc_op[lj] = dc->cc_op; + gen_opc_instr_start[lj] = 1; + } + pc_ptr = disas_insn(dc, pc_ptr); + /* stop translation if indicated */ + if (dc->is_jmp) + break; + /* if single step mode, we generate only one instruction and + generate an exception */ + /* if irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear + the flag and abort the translation to give the irqs a + change to be happen */ + if (dc->tf || dc->singlestep_enabled || + (flags & HF_INHIBIT_IRQ_MASK) || + (cflags & CF_SINGLE_INSN)) { + gen_jmp_im(pc_ptr - dc->cs_base); + gen_eob(dc); + break; + } + /* if too long translation, stop generation too */ + if (gen_opc_ptr >= gen_opc_end || + (pc_ptr - pc_start) >= (TARGET_PAGE_SIZE - 32)) { + gen_jmp_im(pc_ptr - dc->cs_base); + gen_eob(dc); + break; + } + } + *gen_opc_ptr = INDEX_op_end; + /* we don't forget to fill the last values */ + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + lj++; + while (lj <= j) + gen_opc_instr_start[lj++] = 0; + } + +#ifdef DEBUG_DISAS + if (loglevel & CPU_LOG_TB_CPU) { + cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP); + } + if (loglevel & CPU_LOG_TB_IN_ASM) { + int disas_flags; + fprintf(logfile, "----------------\n"); + fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start)); +#ifdef TARGET_X86_64 + if (dc->code64) + disas_flags = 2; + else +#endif + disas_flags = !dc->code32; + target_disas(logfile, pc_start, pc_ptr - pc_start, disas_flags); + fprintf(logfile, "\n"); + if (loglevel & CPU_LOG_TB_OP) { + fprintf(logfile, "OP:\n"); + dump_ops(gen_opc_buf, gen_opparam_buf); + fprintf(logfile, "\n"); + } + } +#endif + + /* optimize flag computations */ + optimize_flags(gen_opc_buf, gen_opc_ptr - gen_opc_buf); + +#ifdef DEBUG_DISAS + if (loglevel & CPU_LOG_TB_OP_OPT) { + fprintf(logfile, "AFTER FLAGS OPT:\n"); + dump_ops(gen_opc_buf, gen_opparam_buf); + fprintf(logfile, "\n"); + } +#endif + if (!search_pc) + tb->size = pc_ptr - pc_start; + return 0; +} + +int gen_intermediate_code(CPUState *env, TranslationBlock *tb) +{ + return gen_intermediate_code_internal(env, tb, 0); +} + +int gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb) +{ + return gen_intermediate_code_internal(env, tb, 1); +} + diff --git a/tools/ioemu/tests/.cvsignore b/tools/ioemu/tests/.cvsignore new file mode 100644 index 0000000000..18a82986cc --- /dev/null +++ b/tools/ioemu/tests/.cvsignore @@ -0,0 +1,23 @@ + gmon.out + testsig + hello-i386 + hello-arm + sha1.test.c + sha1.c + test-i386 + sha1 + testclone + .gdb_history + testthread + test-i386.s + test-i386.ref + sha1-i386 + runcom + debug.com + test-i386.out + speed.txt + test-i386.ref.P3 + pi_10.com + test-i386.ref.P4 + ldso.c + test_path diff --git a/tools/ioemu/tests/CVS/Entries b/tools/ioemu/tests/CVS/Entries new file mode 100644 index 0000000000..50674cddfc --- /dev/null +++ b/tools/ioemu/tests/CVS/Entries @@ -0,0 +1,18 @@ +/.cvsignore/1.4/Wed May 17 14:47:02 2006//Trelease_0_8_1 +/Makefile/1.36/Thu May 25 18:22:40 2006//Trelease_0_8_1 +/hello-arm.c/1.1/Wed May 17 14:47:02 2006//Trelease_0_8_1 +/hello-i386.c/1.1/Wed May 17 14:47:02 2006//Trelease_0_8_1 +/linux-test.c/1.3/Wed May 17 14:47:02 2006//Trelease_0_8_1 +/pi_10.com/1.1/Wed May 17 14:47:02 2006/-kb/Trelease_0_8_1 +/qruncom.c/1.4/Thu May 25 18:22:41 2006//Trelease_0_8_1 +/runcom.c/1.3/Wed May 17 14:47:02 2006//Trelease_0_8_1 +/sha1.c/1.1/Wed May 17 14:47:02 2006//Trelease_0_8_1 +/test-i386-code16.S/1.3/Wed May 24 10:40:26 2006//Trelease_0_8_1 +/test-i386-muldiv.h/1.2/Wed May 24 10:40:26 2006//Trelease_0_8_1 +/test-i386-shift.h/1.5/Thu May 25 18:22:41 2006//Trelease_0_8_1 +/test-i386-vm86.S/1.1/Wed May 17 14:47:02 2006//Trelease_0_8_1 +/test-i386.c/1.49/Wed May 24 10:40:26 2006//Trelease_0_8_1 +/test-i386.h/1.2/Wed May 24 10:40:26 2006//Trelease_0_8_1 +/test_path.c/1.1/Wed May 17 14:47:02 2006//Trelease_0_8_1 +/testthread.c/1.2/Wed May 17 14:47:02 2006//Trelease_0_8_1 +D diff --git a/tools/ioemu/tests/CVS/Repository b/tools/ioemu/tests/CVS/Repository new file mode 100644 index 0000000000..37b7123ea1 --- /dev/null +++ b/tools/ioemu/tests/CVS/Repository @@ -0,0 +1 @@ +qemu/tests diff --git a/tools/ioemu/tests/CVS/Root b/tools/ioemu/tests/CVS/Root new file mode 100644 index 0000000000..e8a9815e6a --- /dev/null +++ b/tools/ioemu/tests/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.savannah.nongnu.org:/sources/qemu diff --git a/tools/ioemu/tests/CVS/Tag b/tools/ioemu/tests/CVS/Tag new file mode 100644 index 0000000000..31c251a4bf --- /dev/null +++ b/tools/ioemu/tests/CVS/Tag @@ -0,0 +1 @@ +Nrelease_0_8_1 diff --git a/tools/ioemu/tests/Makefile b/tools/ioemu/tests/Makefile new file mode 100644 index 0000000000..59a0b6d994 --- /dev/null +++ b/tools/ioemu/tests/Makefile @@ -0,0 +1,92 @@ +-include ../config-host.mak + +CFLAGS=-Wall -O2 -g #-msse2 +LDFLAGS= + +ifeq ($(ARCH),i386) +TESTS=linux-test testthread sha1-i386 test-i386 runcom +endif +ifeq ($(ARCH),x86_64) +TESTS=test-x86_64 +endif +TESTS+=sha1# test_path +#TESTS+=test_path + +QEMU=../i386-user/qemu-i386 + +all: $(TESTS) + +hello-i386: hello-i386.c + $(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $< + strip $@ + +testthread: testthread.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lpthread + +test_path: test_path.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + ./$@ || { rm $@; exit 1; } + +# i386/x86_64 emulation test (test various opcodes) */ +test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S \ + test-i386.h test-i386-shift.h test-i386-muldiv.h + $(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ \ + test-i386.c test-i386-code16.S test-i386-vm86.S -lm + +test-x86_64: test-i386.c \ + test-i386.h test-i386-shift.h test-i386-muldiv.h + $(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ test-i386.c -lm + +ifeq ($(ARCH),i386) +test: test-i386 + ./test-i386 > test-i386.ref +else +test: +endif + $(QEMU) test-i386 > test-i386.out + @if diff -u test-i386.ref test-i386.out ; then echo "Auto Test OK"; fi +ifeq ($(ARCH),i386) + $(QEMU) -no-code-copy test-i386 > test-i386.out + @if diff -u test-i386.ref test-i386.out ; then echo "Auto Test OK (no code copy)"; fi +endif + +# generic Linux and CPU test +linux-test: linux-test.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lm + +# speed test +sha1-i386: sha1.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + +sha1: sha1.c + $(HOST_CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + +speed: sha1 sha1-i386 + time ./sha1 + time $(QEMU) ./sha1-i386 + +# vm86 test +runcom: runcom.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + +# NOTE: -fomit-frame-pointer is currently needed : this is a bug in libqemu +qruncom: qruncom.c ../i386-user/libqemu.a + $(CC) $(CFLAGS) -fomit-frame-pointer $(LDFLAGS) -I../target-i386 -I.. -I../i386-user -I../fpu \ + -o $@ $< -L../i386-user -lqemu -lm + +# arm test +hello-arm: hello-arm.o + arm-linux-ld -o $@ $< + +hello-arm.o: hello-arm.c + arm-linux-gcc -Wall -g -O2 -c -o $@ $< + +# XXX: find a way to compile easily a test for each arch +test2: + @for arch in i386 arm armeb sparc ppc mips mipsel; do \ + ../$${arch}-user/qemu-$${arch} $${arch}/ls -l linux-test.c ; \ + done + +clean: + rm -f *~ *.o test-i386.out test-i386.ref \ + test-x86_64.log test-x86_64.ref qruncom $(TESTS) diff --git a/tools/ioemu/tests/hello-arm.c b/tools/ioemu/tests/hello-arm.c new file mode 100644 index 0000000000..f84e6cb368 --- /dev/null +++ b/tools/ioemu/tests/hello-arm.c @@ -0,0 +1,113 @@ +#define __NR_SYSCALL_BASE 0x900000 +#define __NR_exit1 (__NR_SYSCALL_BASE+ 1) +#define __NR_write (__NR_SYSCALL_BASE+ 4) + +#define __sys2(x) #x +#define __sys1(x) __sys2(x) + +#ifndef __syscall +#define __syscall(name) "swi\t" __sys1(__NR_##name) "\n\t" +#endif + +#define __syscall_return(type, res) \ +do { \ + return (type) (res); \ +} while (0) + +#define _syscall0(type,name) \ +type name(void) { \ + long __res; \ + __asm__ __volatile__ ( \ + __syscall(name) \ + "mov %0,r0" \ + :"=r" (__res) : : "r0","lr"); \ + __syscall_return(type,__res); \ +} + +#define _syscall1(type,name,type1,arg1) \ +type name(type1 arg1) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + __syscall(name) \ + "mov %0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)) \ + : "r0","lr"); \ + __syscall_return(type,__res); \ +} + +#define _syscall2(type,name,type1,arg1,type2,arg2) \ +type name(type1 arg1,type2 arg2) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + "mov\tr1,%2\n\t" \ + __syscall(name) \ + "mov\t%0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)) \ + : "r0","r1","lr"); \ + __syscall_return(type,__res); \ +} + + +#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ +type name(type1 arg1,type2 arg2,type3 arg3) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + "mov\tr1,%2\n\t" \ + "mov\tr2,%3\n\t" \ + __syscall(name) \ + "mov\t%0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)) \ + : "r0","r1","r2","lr"); \ + __syscall_return(type,__res); \ +} + + +#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ +type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + "mov\tr1,%2\n\t" \ + "mov\tr2,%3\n\t" \ + "mov\tr3,%4\n\t" \ + __syscall(name) \ + "mov\t%0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)),"r" ((long)(arg4)) \ + : "r0","r1","r2","r3","lr"); \ + __syscall_return(type,__res); \ +} + + +#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5) \ +type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) { \ + long __res; \ + __asm__ __volatile__ ( \ + "mov\tr0,%1\n\t" \ + "mov\tr1,%2\n\t" \ + "mov\tr2,%3\n\t" \ + "mov\tr3,%4\n\t" \ + "mov\tr4,%5\n\t" \ + __syscall(name) \ + "mov\t%0,r0" \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)),"r" ((long)(arg4)), \ + "r" ((long)(arg5)) \ + : "r0","r1","r2","r3","r4","lr"); \ + __syscall_return(type,__res); \ +} + +_syscall1(int,exit1,int,status); +_syscall3(int,write,int,fd,const char *,buf, int, len); + +void _start(void) +{ + write(1, "Hello World\n", 12); + exit1(0); +} diff --git a/tools/ioemu/tests/hello-i386.c b/tools/ioemu/tests/hello-i386.c new file mode 100644 index 0000000000..e00245d3fe --- /dev/null +++ b/tools/ioemu/tests/hello-i386.c @@ -0,0 +1,26 @@ +#include + +extern inline volatile void exit(int status) +{ + int __res; + __asm__ volatile ("movl %%ecx,%%ebx\n"\ + "int $0x80" \ + : "=a" (__res) : "0" (__NR_exit),"c" ((long)(status))); +} + +extern inline int write(int fd, const char * buf, int len) +{ + int status; + __asm__ volatile ("pushl %%ebx\n"\ + "movl %%esi,%%ebx\n"\ + "int $0x80\n" \ + "popl %%ebx\n"\ + : "=a" (status) \ + : "0" (__NR_write),"S" ((long)(fd)),"c" ((long)(buf)),"d" ((long)(len))); +} + +void _start(void) +{ + write(1, "Hello World\n", 12); + exit(0); +} diff --git a/tools/ioemu/tests/linux-test.c b/tools/ioemu/tests/linux-test.c new file mode 100644 index 0000000000..6ca9029650 --- /dev/null +++ b/tools/ioemu/tests/linux-test.c @@ -0,0 +1,536 @@ +/* + * linux and CPU test + * + * Copyright (c) 2003 Fabrice Bellard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TESTPATH "/tmp/linux-test.tmp" +#define TESTPORT 7654 +#define STACK_SIZE 16384 + +void error1(const char *filename, int line, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d: ", filename, line); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} + +int __chk_error(const char *filename, int line, int ret) +{ + if (ret < 0) { + error1(filename, line, "%m (ret=%d, errno=%d)", + ret, errno); + } + return ret; +} + +#define error(fmt, args...) error1(__FILE__, __LINE__, fmt, ##args) + +#define chk_error(ret) __chk_error(__FILE__, __LINE__, (ret)) + +/*******************************************************/ + +#define FILE_BUF_SIZE 300 + +void test_file(void) +{ + int fd, i, len, ret; + uint8_t buf[FILE_BUF_SIZE]; + uint8_t buf2[FILE_BUF_SIZE]; + uint8_t buf3[FILE_BUF_SIZE]; + char cur_dir[1024]; + struct stat st; + struct utimbuf tbuf; + struct iovec vecs[2]; + DIR *dir; + struct dirent *de; + + /* clean up, just in case */ + unlink(TESTPATH "/file1"); + unlink(TESTPATH "/file2"); + unlink(TESTPATH "/file3"); + rmdir(TESTPATH); + + if (getcwd(cur_dir, sizeof(cur_dir)) == NULL) + error("getcwd"); + + chk_error(mkdir(TESTPATH, 0755)); + + chk_error(chdir(TESTPATH)); + + /* open/read/write/close/readv/writev/lseek */ + + fd = chk_error(open("file1", O_WRONLY | O_TRUNC | O_CREAT, 0644)); + for(i=0;i < FILE_BUF_SIZE; i++) + buf[i] = i; + len = chk_error(write(fd, buf, FILE_BUF_SIZE / 2)); + if (len != (FILE_BUF_SIZE / 2)) + error("write"); + vecs[0].iov_base = buf + (FILE_BUF_SIZE / 2); + vecs[0].iov_len = 16; + vecs[1].iov_base = buf + (FILE_BUF_SIZE / 2) + 16; + vecs[1].iov_len = (FILE_BUF_SIZE / 2) - 16; + len = chk_error(writev(fd, vecs, 2)); + if (len != (FILE_BUF_SIZE / 2)) + error("writev"); + chk_error(close(fd)); + + chk_error(rename("file1", "file2")); + + fd = chk_error(open("file2", O_RDONLY)); + + len = chk_error(read(fd, buf2, FILE_BUF_SIZE)); + if (len != FILE_BUF_SIZE) + error("read"); + if (memcmp(buf, buf2, FILE_BUF_SIZE) != 0) + error("memcmp"); + +#define FOFFSET 16 + ret = chk_error(lseek(fd, FOFFSET, SEEK_SET)); + if (ret != 16) + error("lseek"); + vecs[0].iov_base = buf3; + vecs[0].iov_len = 32; + vecs[1].iov_base = buf3 + 32; + vecs[1].iov_len = FILE_BUF_SIZE - FOFFSET - 32; + len = chk_error(readv(fd, vecs, 2)); + if (len != FILE_BUF_SIZE - FOFFSET) + error("readv"); + if (memcmp(buf + FOFFSET, buf3, FILE_BUF_SIZE - FOFFSET) != 0) + error("memcmp"); + + chk_error(close(fd)); + + /* access */ + chk_error(access("file2", R_OK)); + + /* stat/chmod/utime/truncate */ + + chk_error(chmod("file2", 0600)); + tbuf.actime = 1001; + tbuf.modtime = 1000; + chk_error(truncate("file2", 100)); + chk_error(utime("file2", &tbuf)); + chk_error(stat("file2", &st)); + if (st.st_size != 100) + error("stat size"); + if (!S_ISREG(st.st_mode)) + error("stat mode"); + if ((st.st_mode & 0777) != 0600) + error("stat mode2"); + if (st.st_atime != 1001 || + st.st_mtime != 1000) + error("stat time"); + + chk_error(stat(TESTPATH, &st)); + if (!S_ISDIR(st.st_mode)) + error("stat mode"); + + /* fstat */ + fd = chk_error(open("file2", O_RDWR)); + chk_error(ftruncate(fd, 50)); + chk_error(fstat(fd, &st)); + chk_error(close(fd)); + + if (st.st_size != 50) + error("stat size"); + if (!S_ISREG(st.st_mode)) + error("stat mode"); + + /* symlink/lstat */ + chk_error(symlink("file2", "file3")); + chk_error(lstat("file3", &st)); + if (!S_ISLNK(st.st_mode)) + error("stat mode"); + + /* getdents */ + dir = opendir(TESTPATH); + if (!dir) + error("opendir"); + len = 0; + for(;;) { + de = readdir(dir); + if (!de) + break; + if (strcmp(de->d_name, ".") != 0 && + strcmp(de->d_name, "..") != 0 && + strcmp(de->d_name, "file2") != 0 && + strcmp(de->d_name, "file3") != 0) + error("readdir"); + len++; + } + closedir(dir); + if (len != 4) + error("readdir"); + + chk_error(unlink("file3")); + chk_error(unlink("file2")); + chk_error(chdir(cur_dir)); + chk_error(rmdir(TESTPATH)); +} + +void test_fork(void) +{ + int pid, status; + + pid = chk_error(fork()); + if (pid == 0) { + /* child */ + exit(2); + } + chk_error(waitpid(pid, &status, 0)); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 2) + error("waitpid status=0x%x", status); +} + +void test_time(void) +{ + struct timeval tv, tv2; + struct timespec ts, rem; + struct rusage rusg1, rusg2; + int ti, i; + + chk_error(gettimeofday(&tv, NULL)); + rem.tv_sec = 1; + ts.tv_sec = 0; + ts.tv_nsec = 20 * 1000000; + chk_error(nanosleep(&ts, &rem)); + if (rem.tv_sec != 1) + error("nanosleep"); + chk_error(gettimeofday(&tv2, NULL)); + ti = tv2.tv_sec - tv.tv_sec; + if (ti >= 2) + error("gettimeofday"); + + chk_error(getrusage(RUSAGE_SELF, &rusg1)); + for(i = 0;i < 10000; i++); + chk_error(getrusage(RUSAGE_SELF, &rusg2)); + if ((rusg2.ru_utime.tv_sec - rusg1.ru_utime.tv_sec) < 0 || + (rusg2.ru_stime.tv_sec - rusg1.ru_stime.tv_sec) < 0) + error("getrusage"); +} + +void pstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + if (buf_size <= 0) + return; + + for(;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = c; + } + *q = '\0'; +} + +/* strcat and truncate. */ +char *pstrcat(char *buf, int buf_size, const char *s) +{ + int len; + len = strlen(buf); + if (len < buf_size) + pstrcpy(buf + len, buf_size - len, s); + return buf; +} + +int server_socket(void) +{ + int val, fd; + struct sockaddr_in sockaddr; + + /* server socket */ + fd = chk_error(socket(PF_INET, SOCK_STREAM, 0)); + + val = 1; + chk_error(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))); + + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(TESTPORT); + sockaddr.sin_addr.s_addr = 0; + chk_error(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))); + chk_error(listen(fd, 0)); + return fd; + +} + +int client_socket(void) +{ + int fd; + struct sockaddr_in sockaddr; + + /* server socket */ + fd = chk_error(socket(PF_INET, SOCK_STREAM, 0)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(TESTPORT); + inet_aton("127.0.0.1", &sockaddr.sin_addr); + chk_error(connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))); + return fd; +} + +const char socket_msg[] = "hello socket\n"; + +void test_socket(void) +{ + int server_fd, client_fd, fd, pid, ret, val; + struct sockaddr_in sockaddr; + socklen_t len; + char buf[512]; + + server_fd = server_socket(); + + /* test a few socket options */ + len = sizeof(val); + chk_error(getsockopt(server_fd, SOL_SOCKET, SO_TYPE, &val, &len)); + if (val != SOCK_STREAM) + error("getsockopt"); + + pid = chk_error(fork()); + if (pid == 0) { + client_fd = client_socket(); + send(client_fd, socket_msg, sizeof(socket_msg), 0); + close(client_fd); + exit(0); + } + len = sizeof(sockaddr); + fd = chk_error(accept(server_fd, (struct sockaddr *)&sockaddr, &len)); + + ret = chk_error(recv(fd, buf, sizeof(buf), 0)); + if (ret != sizeof(socket_msg)) + error("recv"); + if (memcmp(buf, socket_msg, sizeof(socket_msg)) != 0) + error("socket_msg"); + chk_error(close(fd)); + chk_error(close(server_fd)); +} + +#define WCOUNT_MAX 512 + +void test_pipe(void) +{ + fd_set rfds, wfds; + int fds[2], fd_max, ret; + uint8_t ch; + int wcount, rcount; + + chk_error(pipe(fds)); + chk_error(fcntl(fds[0], F_SETFL, O_NONBLOCK)); + chk_error(fcntl(fds[1], F_SETFL, O_NONBLOCK)); + wcount = 0; + rcount = 0; + for(;;) { + FD_ZERO(&rfds); + fd_max = fds[0]; + FD_SET(fds[0], &rfds); + + FD_ZERO(&wfds); + FD_SET(fds[1], &wfds); + if (fds[1] > fd_max) + fd_max = fds[1]; + + ret = chk_error(select(fd_max + 1, &rfds, &wfds, NULL, NULL)); + if (ret > 0) { + if (FD_ISSET(fds[0], &rfds)) { + chk_error(read(fds[0], &ch, 1)); + rcount++; + if (rcount >= WCOUNT_MAX) + break; + } + if (FD_ISSET(fds[1], &wfds)) { + ch = 'a'; + chk_error(write(fds[0], &ch, 1)); + wcount++; + } + } + } + chk_error(close(fds[0])); + chk_error(close(fds[1])); +} + +int thread1_res; +int thread2_res; + +int thread1_func(void *arg) +{ + int i; + for(i=0;i<5;i++) { + thread1_res++; + usleep(10 * 1000); + } + return 0; +} + +int thread2_func(void *arg) +{ + int i; + for(i=0;i<6;i++) { + thread2_res++; + usleep(10 * 1000); + } + return 0; +} + +void test_clone(void) +{ + uint8_t *stack1, *stack2; + int pid1, pid2, status1, status2; + + stack1 = malloc(STACK_SIZE); + pid1 = chk_error(clone(thread1_func, stack1 + STACK_SIZE, + CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello1")); + + stack2 = malloc(STACK_SIZE); + pid2 = chk_error(clone(thread2_func, stack2 + STACK_SIZE, + CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello2")); + + while (waitpid(pid1, &status1, 0) != pid1); + while (waitpid(pid2, &status2, 0) != pid2); + if (thread1_res != 5 || + thread2_res != 6) + error("clone"); +} + +/***********************************/ + +volatile int alarm_count; +jmp_buf jmp_env; + +void sig_alarm(int sig) +{ + if (sig != SIGALRM) + error("signal"); + alarm_count++; +} + +void sig_segv(int sig, siginfo_t *info, void *puc) +{ + if (sig != SIGSEGV) + error("signal"); + longjmp(jmp_env, 1); +} + +void test_signal(void) +{ + struct sigaction act; + struct itimerval it, oit; + + /* timer test */ + + alarm_count = 0; + + act.sa_handler = sig_alarm; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + chk_error(sigaction(SIGALRM, &act, NULL)); + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 10 * 1000; + it.it_value.tv_sec = 0; + it.it_value.tv_usec = 10 * 1000; + chk_error(setitimer(ITIMER_REAL, &it, NULL)); + chk_error(getitimer(ITIMER_REAL, &oit)); + if (oit.it_value.tv_sec != it.it_value.tv_sec || + oit.it_value.tv_usec != it.it_value.tv_usec) + error("itimer"); + + while (alarm_count < 5) { + usleep(10 * 1000); + } + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = 0; + it.it_value.tv_usec = 0; + memset(&oit, 0xff, sizeof(oit)); + chk_error(setitimer(ITIMER_REAL, &it, &oit)); + if (oit.it_value.tv_sec != 0 || + oit.it_value.tv_usec != 10 * 1000) + error("setitimer"); + + /* SIGSEGV test */ + act.sa_sigaction = sig_segv; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + chk_error(sigaction(SIGSEGV, &act, NULL)); + if (setjmp(jmp_env) == 0) { + *(uint8_t *)0 = 0; + } + + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + chk_error(sigaction(SIGSEGV, &act, NULL)); +} + +#define SHM_SIZE 32768 + +void test_shm(void) +{ + void *ptr; + int shmid; + + shmid = chk_error(shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0777)); + ptr = shmat(shmid, NULL, 0); + if (!ptr) + error("shmat"); + + memset(ptr, 0, SHM_SIZE); + + chk_error(shmctl(shmid, IPC_RMID, 0)); + chk_error(shmdt(ptr)); +} + +int main(int argc, char **argv) +{ + test_file(); + test_fork(); + test_time(); + test_socket(); + // test_clone(); + test_signal(); + test_shm(); + return 0; +} diff --git a/tools/ioemu/tests/qruncom.c b/tools/ioemu/tests/qruncom.c new file mode 100644 index 0000000000..421e6a99f2 --- /dev/null +++ b/tools/ioemu/tests/qruncom.c @@ -0,0 +1,327 @@ +/* + * Example of use of user mode libqemu: launch a basic .com DOS + * executable + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpu.h" + +//#define SIGTEST + +void cpu_outb(CPUState *env, int addr, int val) +{ + fprintf(stderr, "outb: port=0x%04x, data=%02x\n", addr, val); +} + +void cpu_outw(CPUState *env, int addr, int val) +{ + fprintf(stderr, "outw: port=0x%04x, data=%04x\n", addr, val); +} + +void cpu_outl(CPUState *env, int addr, int val) +{ + fprintf(stderr, "outl: port=0x%04x, data=%08x\n", addr, val); +} + +int cpu_inb(CPUState *env, int addr) +{ + fprintf(stderr, "inb: port=0x%04x\n", addr); + return 0; +} + +int cpu_inw(CPUState *env, int addr) +{ + fprintf(stderr, "inw: port=0x%04x\n", addr); + return 0; +} + +int cpu_inl(CPUState *env, int addr) +{ + fprintf(stderr, "inl: port=0x%04x\n", addr); + return 0; +} + +int cpu_get_pic_interrupt(CPUState *env) +{ + return -1; +} + +uint64_t cpu_get_tsc(CPUState *env) +{ + return 0; +} + +static void set_gate(void *ptr, unsigned int type, unsigned int dpl, + unsigned long addr, unsigned int sel) +{ + unsigned int e1, e2; + e1 = (addr & 0xffff) | (sel << 16); + e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); + stl((uint8_t *)ptr, e1); + stl((uint8_t *)ptr + 4, e2); +} + +uint64_t idt_table[256]; + +/* only dpl matters as we do only user space emulation */ +static void set_idt(int n, unsigned int dpl) +{ + set_gate(idt_table + n, 0, dpl, 0, 0); +} + +void qemu_free(void *ptr) +{ + free(ptr); +} + +void *qemu_malloc(size_t size) +{ + return malloc(size); +} + +void *qemu_mallocz(size_t size) +{ + void *ptr; + ptr = qemu_malloc(size); + if (!ptr) + return NULL; + memset(ptr, 0, size); + return ptr; +} + +void *qemu_vmalloc(size_t size) +{ + return memalign(4096, size); +} + +void qemu_vfree(void *ptr) +{ + free(ptr); +} + +void qemu_printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +/* XXX: this is a bug in helper2.c */ +int errno; + +/**********************************************/ + +#define COM_BASE_ADDR 0x10100 + +void usage(void) +{ + printf("qruncom version 0.1 (c) 2003 Fabrice Bellard\n" + "usage: qruncom file.com\n" + "user mode libqemu demo: run simple .com DOS executables\n"); + exit(1); +} + +static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg) +{ + return (uint8_t *)((seg << 4) + (reg & 0xffff)); +} + +static inline void pushw(CPUState *env, int val) +{ + env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | ((env->regs[R_ESP] - 2) & 0xffff); + *(uint16_t *)seg_to_linear(env->segs[R_SS].selector, env->regs[R_ESP]) = val; +} + +static void host_segv_handler(int host_signum, siginfo_t *info, + void *puc) +{ + if (cpu_signal_handler(host_signum, info, puc)) { + return; + } + abort(); +} + +int main(int argc, char **argv) +{ + uint8_t *vm86_mem; + const char *filename; + int fd, ret, seg; + CPUState *env; + + if (argc != 2) + usage(); + filename = argv[1]; + + vm86_mem = mmap((void *)0x00000000, 0x110000, + PROT_WRITE | PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); + if (vm86_mem == MAP_FAILED) { + perror("mmap"); + exit(1); + } + + /* load the MSDOS .com executable */ + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror(filename); + exit(1); + } + ret = read(fd, vm86_mem + COM_BASE_ADDR, 65536 - 256); + if (ret < 0) { + perror("read"); + exit(1); + } + close(fd); + + /* install exception handler for CPU emulator */ + { + struct sigaction act; + + sigfillset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + // act.sa_flags |= SA_ONSTACK; + + act.sa_sigaction = host_segv_handler; + sigaction(SIGSEGV, &act, NULL); + sigaction(SIGBUS, &act, NULL); +#if defined (TARGET_I386) && defined(USE_CODE_COPY) + sigaction(SIGFPE, &act, NULL); +#endif + } + + // cpu_set_log(CPU_LOG_TB_IN_ASM | CPU_LOG_TB_OUT_ASM | CPU_LOG_EXEC); + + env = cpu_init(); + + /* disable code copy to simplify debugging */ + code_copy_enabled = 0; + + /* set user mode state (XXX: should be done automatically by + cpu_init ?) */ + env->user_mode_only = 1; + + cpu_x86_set_cpl(env, 3); + + env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; + /* NOTE: hflags duplicates some of the virtual CPU state */ + env->hflags |= HF_PE_MASK | VM_MASK; + + /* flags setup : we activate the IRQs by default as in user + mode. We also activate the VM86 flag to run DOS code */ + env->eflags |= IF_MASK | VM_MASK; + + /* init basic registers */ + env->eip = 0x100; + env->regs[R_ESP] = 0xfffe; + seg = (COM_BASE_ADDR - 0x100) >> 4; + + cpu_x86_load_seg_cache(env, R_CS, seg, + (seg << 4), 0xffff, 0); + cpu_x86_load_seg_cache(env, R_SS, seg, + (seg << 4), 0xffff, 0); + cpu_x86_load_seg_cache(env, R_DS, seg, + (seg << 4), 0xffff, 0); + cpu_x86_load_seg_cache(env, R_ES, seg, + (seg << 4), 0xffff, 0); + cpu_x86_load_seg_cache(env, R_FS, seg, + (seg << 4), 0xffff, 0); + cpu_x86_load_seg_cache(env, R_GS, seg, + (seg << 4), 0xffff, 0); + + /* exception support */ + env->idt.base = (unsigned long)idt_table; + env->idt.limit = sizeof(idt_table) - 1; + set_idt(0, 0); + set_idt(1, 0); + set_idt(2, 0); + set_idt(3, 3); + set_idt(4, 3); + set_idt(5, 3); + set_idt(6, 0); + set_idt(7, 0); + set_idt(8, 0); + set_idt(9, 0); + set_idt(10, 0); + set_idt(11, 0); + set_idt(12, 0); + set_idt(13, 0); + set_idt(14, 0); + set_idt(15, 0); + set_idt(16, 0); + set_idt(17, 0); + set_idt(18, 0); + set_idt(19, 0); + + /* put return code */ + *seg_to_linear(env->segs[R_CS].selector, 0) = 0xb4; /* mov ah, $0 */ + *seg_to_linear(env->segs[R_CS].selector, 1) = 0x00; + *seg_to_linear(env->segs[R_CS].selector, 2) = 0xcd; /* int $0x21 */ + *seg_to_linear(env->segs[R_CS].selector, 3) = 0x21; + pushw(env, 0x0000); + + /* the value of these registers seem to be assumed by pi_10.com */ + env->regs[R_ESI] = 0x100; + env->regs[R_ECX] = 0xff; + env->regs[R_EBP] = 0x0900; + env->regs[R_EDI] = 0xfffe; + + /* inform the emulator of the mmaped memory */ + page_set_flags(0x00000000, 0x110000, + PAGE_WRITE | PAGE_READ | PAGE_EXEC | PAGE_VALID); + + for(;;) { + ret = cpu_x86_exec(env); + switch(ret) { + case EXCP0D_GPF: + { + int int_num, ah; + int_num = *(uint8_t *)(env->segs[R_CS].base + env->eip + 1); + if (int_num != 0x21) + goto unknown_int; + ah = (env->regs[R_EAX] >> 8) & 0xff; + switch(ah) { + case 0x00: /* exit */ + exit(0); + case 0x02: /* write char */ + { + uint8_t c = env->regs[R_EDX]; + write(1, &c, 1); + } + break; + case 0x09: /* write string */ + { + uint8_t c; + for(;;) { + c = *seg_to_linear(env->segs[R_DS].selector, env->regs[R_EAX]); + if (c == '$') + break; + write(1, &c, 1); + } + env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | '$'; + } + break; + default: + unknown_int: + fprintf(stderr, "unsupported int 0x%02x\n", int_num); + cpu_dump_state(env, stderr, fprintf, 0); + // exit(1); + } + env->eip += 2; + } + break; + default: + fprintf(stderr, "unhandled cpu_exec return code (0x%x)\n", ret); + cpu_dump_state(env, stderr, fprintf, 0); + exit(1); + } + } +} diff --git a/tools/ioemu/tests/runcom.c b/tools/ioemu/tests/runcom.c new file mode 100644 index 0000000000..43deeca098 --- /dev/null +++ b/tools/ioemu/tests/runcom.c @@ -0,0 +1,195 @@ +/* + * Simple example of use of vm86: launch a basic .com DOS executable + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//#define SIGTEST + +#undef __syscall_return +#define __syscall_return(type, res) \ +do { \ + return (type) (res); \ +} while (0) + +_syscall2(int, vm86, int, func, struct vm86plus_struct *, v86) + +#define COM_BASE_ADDR 0x10100 + +void usage(void) +{ + printf("runcom version 0.1 (c) 2003 Fabrice Bellard\n" + "usage: runcom file.com\n" + "VM86 Run simple .com DOS executables (linux vm86 test mode)\n"); + exit(1); +} + +static inline void set_bit(uint8_t *a, unsigned int bit) +{ + a[bit / 8] |= (1 << (bit % 8)); +} + +static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg) +{ + return (uint8_t *)((seg << 4) + (reg & 0xffff)); +} + +static inline void pushw(struct vm86_regs *r, int val) +{ + r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff); + *(uint16_t *)seg_to_linear(r->ss, r->esp) = val; +} + +void dump_regs(struct vm86_regs *r) +{ + fprintf(stderr, + "EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n" + "ESI=%08lx EDI=%08lx EBP=%08lx ESP=%08lx\n" + "EIP=%08lx EFL=%08lx\n" + "CS=%04x DS=%04x ES=%04x SS=%04x FS=%04x GS=%04x\n", + r->eax, r->ebx, r->ecx, r->edx, r->esi, r->edi, r->ebp, r->esp, + r->eip, r->eflags, + r->cs, r->ds, r->es, r->ss, r->fs, r->gs); +} + +#ifdef SIGTEST +void alarm_handler(int sig) +{ + fprintf(stderr, "alarm signal=%d\n", sig); + alarm(1); +} +#endif + +int main(int argc, char **argv) +{ + uint8_t *vm86_mem; + const char *filename; + int fd, ret, seg; + struct vm86plus_struct ctx; + struct vm86_regs *r; + + if (argc != 2) + usage(); + filename = argv[1]; + + vm86_mem = mmap((void *)0x00000000, 0x110000, + PROT_WRITE | PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); + if (vm86_mem == MAP_FAILED) { + perror("mmap"); + exit(1); + } +#ifdef SIGTEST + { + struct sigaction act; + + act.sa_handler = alarm_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGALRM, &act, NULL); + alarm(1); + } +#endif + + /* load the MSDOS .com executable */ + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror(filename); + exit(1); + } + ret = read(fd, vm86_mem + COM_BASE_ADDR, 65536 - 256); + if (ret < 0) { + perror("read"); + exit(1); + } + close(fd); + + memset(&ctx, 0, sizeof(ctx)); + /* init basic registers */ + r = &ctx.regs; + r->eip = 0x100; + r->esp = 0xfffe; + seg = (COM_BASE_ADDR - 0x100) >> 4; + r->cs = seg; + r->ss = seg; + r->ds = seg; + r->es = seg; + r->fs = seg; + r->gs = seg; + r->eflags = VIF_MASK; + + /* put return code */ + set_bit((uint8_t *)&ctx.int_revectored, 0x21); + *seg_to_linear(r->cs, 0) = 0xb4; /* mov ah, $0 */ + *seg_to_linear(r->cs, 1) = 0x00; + *seg_to_linear(r->cs, 2) = 0xcd; /* int $0x21 */ + *seg_to_linear(r->cs, 3) = 0x21; + pushw(&ctx.regs, 0x0000); + + /* the value of these registers seem to be assumed by pi_10.com */ + r->esi = 0x100; + r->ecx = 0xff; + r->ebp = 0x0900; + r->edi = 0xfffe; + + for(;;) { + ret = vm86(VM86_ENTER, &ctx); + switch(VM86_TYPE(ret)) { + case VM86_INTx: + { + int int_num, ah; + + int_num = VM86_ARG(ret); + if (int_num != 0x21) + goto unknown_int; + ah = (r->eax >> 8) & 0xff; + switch(ah) { + case 0x00: /* exit */ + exit(0); + case 0x02: /* write char */ + { + uint8_t c = r->edx; + write(1, &c, 1); + } + break; + case 0x09: /* write string */ + { + uint8_t c; + for(;;) { + c = *seg_to_linear(r->ds, r->edx); + if (c == '$') + break; + write(1, &c, 1); + } + r->eax = (r->eax & ~0xff) | '$'; + } + break; + default: + unknown_int: + fprintf(stderr, "unsupported int 0x%02x\n", int_num); + dump_regs(&ctx.regs); + // exit(1); + } + } + break; + case VM86_SIGNAL: + /* a signal came, we just ignore that */ + break; + case VM86_STI: + break; + default: + fprintf(stderr, "unhandled vm86 return code (0x%x)\n", ret); + dump_regs(&ctx.regs); + exit(1); + } + } +} diff --git a/tools/ioemu/tests/sha1.c b/tools/ioemu/tests/sha1.c new file mode 100644 index 0000000000..31b001920d --- /dev/null +++ b/tools/ioemu/tests/sha1.c @@ -0,0 +1,242 @@ + +/* from valgrind tests */ + +/* ================ sha1.c ================ */ +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#define SHA1HANDSOFF + +#include +#include +#include /* for u_int*_t */ + +/* ================ sha1.h ================ */ +/* +SHA-1 in C +By Steve Reid +100% Public Domain +*/ + +typedef struct { + u_int32_t state[5]; + u_int32_t count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]); +void SHA1Init(SHA1_CTX* context); +void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len); +void SHA1Final(unsigned char digest[20], SHA1_CTX* context); +/* ================ end of sha1.h ================ */ +#include + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#elif BYTE_ORDER == BIG_ENDIAN +#define blk0(i) block->l[i] +#else +#error "Endianness not defined!" +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]) +{ +u_int32_t a, b, c, d, e; +typedef union { + unsigned char c[64]; + u_int32_t l[16]; +} CHAR64LONG16; +#ifdef SHA1HANDSOFF +CHAR64LONG16 block[1]; /* use array to appear as a pointer */ + memcpy(block, buffer, 64); +#else + /* The following had better never be used because it causes the + * pointer-to-const buffer to be cast into a pointer to non-const. + * And the result is written through. I threw a "const" in, hoping + * this will cause a diagnostic. + */ +CHAR64LONG16* block = (const CHAR64LONG16*)buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +#ifdef SHA1HANDSOFF + memset(block, '\0', sizeof(block)); +#endif +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len) +{ +u_int32_t i; +u_int32_t j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1]++; + context->count[1] += (len>>29); + j = (j >> 3) & 63; + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +{ +unsigned i; +unsigned char finalcount[8]; +unsigned char c; + +#if 0 /* untested "improvement" by DHR */ + /* Convert context->count to a sequence of bytes + * in finalcount. Second element first, but + * big-endian order within element. + * But we do it all backwards. + */ + unsigned char *fcp = &finalcount[8]; + + for (i = 0; i < 2; i++) + { + u_int32_t t = context->count[i]; + int j; + + for (j = 0; j < 4; t >>= 8, j++) + *--fcp = (unsigned char) t + } +#else + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } +#endif + c = 0200; + SHA1Update(context, &c, 1); + while ((context->count[0] & 504) != 448) { + c = 0000; + SHA1Update(context, &c, 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + memset(context, '\0', sizeof(*context)); + memset(&finalcount, '\0', sizeof(finalcount)); +} +/* ================ end of sha1.c ================ */ + +#define BUFSIZE 4096 + +int +main(int argc, char **argv) +{ + SHA1_CTX ctx; + unsigned char hash[20], buf[BUFSIZE]; + int i; + + for(i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(__x86_64__) +#define TEST_VM86 +#define TEST_SEGS +#endif +//#define LINUX_VM86_IOPL_FIX +//#define TEST_P4_FLAGS +#if defined(__x86_64__) +#define TEST_SSE +#define TEST_CMOV 1 +#define TEST_FCOMI 1 +#else +//#define TEST_SSE +#define TEST_CMOV 0 +#define TEST_FCOMI 0 +#endif + +#if defined(__x86_64__) +#define FMT64X "%016lx" +#define FMTLX "%016lx" +#define X86_64_ONLY(x) x +#else +#define FMT64X "%016llx" +#define FMTLX "%08lx" +#define X86_64_ONLY(x) +#endif + +#ifdef TEST_VM86 +#include +#endif + +#define xglue(x, y) x ## y +#define glue(x, y) xglue(x, y) +#define stringify(s) tostring(s) +#define tostring(s) #s + +#define CC_C 0x0001 +#define CC_P 0x0004 +#define CC_A 0x0010 +#define CC_Z 0x0040 +#define CC_S 0x0080 +#define CC_O 0x0800 + +#define __init_call __attribute__ ((unused,__section__ ("initcall"))) + +#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A) + +#if defined(__x86_64__) +static inline long i2l(long v) +{ + return v | ((v ^ 0xabcd) << 32); +} +#else +static inline long i2l(long v) +{ + return v; +} +#endif + +#define OP add +#include "test-i386.h" + +#define OP sub +#include "test-i386.h" + +#define OP xor +#include "test-i386.h" + +#define OP and +#include "test-i386.h" + +#define OP or +#include "test-i386.h" + +#define OP cmp +#include "test-i386.h" + +#define OP adc +#define OP_CC +#include "test-i386.h" + +#define OP sbb +#define OP_CC +#include "test-i386.h" + +#define OP inc +#define OP_CC +#define OP1 +#include "test-i386.h" + +#define OP dec +#define OP_CC +#define OP1 +#include "test-i386.h" + +#define OP neg +#define OP_CC +#define OP1 +#include "test-i386.h" + +#define OP not +#define OP_CC +#define OP1 +#include "test-i386.h" + +#undef CC_MASK +#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O) + +#define OP shl +#include "test-i386-shift.h" + +#define OP shr +#include "test-i386-shift.h" + +#define OP sar +#include "test-i386-shift.h" + +#define OP rol +#include "test-i386-shift.h" + +#define OP ror +#include "test-i386-shift.h" + +#define OP rcr +#define OP_CC +#include "test-i386-shift.h" + +#define OP rcl +#define OP_CC +#include "test-i386-shift.h" + +#define OP shld +#define OP_SHIFTD +#define OP_NOBYTE +#include "test-i386-shift.h" + +#define OP shrd +#define OP_SHIFTD +#define OP_NOBYTE +#include "test-i386-shift.h" + +/* XXX: should be more precise ? */ +#undef CC_MASK +#define CC_MASK (CC_C) + +#define OP bt +#define OP_NOBYTE +#include "test-i386-shift.h" + +#define OP bts +#define OP_NOBYTE +#include "test-i386-shift.h" + +#define OP btr +#define OP_NOBYTE +#include "test-i386-shift.h" + +#define OP btc +#define OP_NOBYTE +#include "test-i386-shift.h" + +/* lea test (modrm support) */ +#define TEST_LEAQ(STR)\ +{\ + asm("lea " STR ", %0"\ + : "=r" (res)\ + : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));\ + printf("lea %s = " FMTLX "\n", STR, res);\ +} + +#define TEST_LEA(STR)\ +{\ + asm("lea " STR ", %0"\ + : "=r" (res)\ + : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));\ + printf("lea %s = " FMTLX "\n", STR, res);\ +} + +#define TEST_LEA16(STR)\ +{\ + asm(".code16 ; .byte 0x67 ; leal " STR ", %0 ; .code32"\ + : "=wq" (res)\ + : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));\ + printf("lea %s = %08lx\n", STR, res);\ +} + + +void test_lea(void) +{ + long eax, ebx, ecx, edx, esi, edi, res; + eax = i2l(0x0001); + ebx = i2l(0x0002); + ecx = i2l(0x0004); + edx = i2l(0x0008); + esi = i2l(0x0010); + edi = i2l(0x0020); + + TEST_LEA("0x4000"); + + TEST_LEA("(%%eax)"); + TEST_LEA("(%%ebx)"); + TEST_LEA("(%%ecx)"); + TEST_LEA("(%%edx)"); + TEST_LEA("(%%esi)"); + TEST_LEA("(%%edi)"); + + TEST_LEA("0x40(%%eax)"); + TEST_LEA("0x40(%%ebx)"); + TEST_LEA("0x40(%%ecx)"); + TEST_LEA("0x40(%%edx)"); + TEST_LEA("0x40(%%esi)"); + TEST_LEA("0x40(%%edi)"); + + TEST_LEA("0x4000(%%eax)"); + TEST_LEA("0x4000(%%ebx)"); + TEST_LEA("0x4000(%%ecx)"); + TEST_LEA("0x4000(%%edx)"); + TEST_LEA("0x4000(%%esi)"); + TEST_LEA("0x4000(%%edi)"); + + TEST_LEA("(%%eax, %%ecx)"); + TEST_LEA("(%%ebx, %%edx)"); + TEST_LEA("(%%ecx, %%ecx)"); + TEST_LEA("(%%edx, %%ecx)"); + TEST_LEA("(%%esi, %%ecx)"); + TEST_LEA("(%%edi, %%ecx)"); + + TEST_LEA("0x40(%%eax, %%ecx)"); + TEST_LEA("0x4000(%%ebx, %%edx)"); + + TEST_LEA("(%%ecx, %%ecx, 2)"); + TEST_LEA("(%%edx, %%ecx, 4)"); + TEST_LEA("(%%esi, %%ecx, 8)"); + + TEST_LEA("(,%%eax, 2)"); + TEST_LEA("(,%%ebx, 4)"); + TEST_LEA("(,%%ecx, 8)"); + + TEST_LEA("0x40(,%%eax, 2)"); + TEST_LEA("0x40(,%%ebx, 4)"); + TEST_LEA("0x40(,%%ecx, 8)"); + + + TEST_LEA("-10(%%ecx, %%ecx, 2)"); + TEST_LEA("-10(%%edx, %%ecx, 4)"); + TEST_LEA("-10(%%esi, %%ecx, 8)"); + + TEST_LEA("0x4000(%%ecx, %%ecx, 2)"); + TEST_LEA("0x4000(%%edx, %%ecx, 4)"); + TEST_LEA("0x4000(%%esi, %%ecx, 8)"); + +#if defined(__x86_64__) + TEST_LEAQ("0x4000"); + TEST_LEAQ("0x4000(%%rip)"); + + TEST_LEAQ("(%%rax)"); + TEST_LEAQ("(%%rbx)"); + TEST_LEAQ("(%%rcx)"); + TEST_LEAQ("(%%rdx)"); + TEST_LEAQ("(%%rsi)"); + TEST_LEAQ("(%%rdi)"); + + TEST_LEAQ("0x40(%%rax)"); + TEST_LEAQ("0x40(%%rbx)"); + TEST_LEAQ("0x40(%%rcx)"); + TEST_LEAQ("0x40(%%rdx)"); + TEST_LEAQ("0x40(%%rsi)"); + TEST_LEAQ("0x40(%%rdi)"); + + TEST_LEAQ("0x4000(%%rax)"); + TEST_LEAQ("0x4000(%%rbx)"); + TEST_LEAQ("0x4000(%%rcx)"); + TEST_LEAQ("0x4000(%%rdx)"); + TEST_LEAQ("0x4000(%%rsi)"); + TEST_LEAQ("0x4000(%%rdi)"); + + TEST_LEAQ("(%%rax, %%rcx)"); + TEST_LEAQ("(%%rbx, %%rdx)"); + TEST_LEAQ("(%%rcx, %%rcx)"); + TEST_LEAQ("(%%rdx, %%rcx)"); + TEST_LEAQ("(%%rsi, %%rcx)"); + TEST_LEAQ("(%%rdi, %%rcx)"); + + TEST_LEAQ("0x40(%%rax, %%rcx)"); + TEST_LEAQ("0x4000(%%rbx, %%rdx)"); + + TEST_LEAQ("(%%rcx, %%rcx, 2)"); + TEST_LEAQ("(%%rdx, %%rcx, 4)"); + TEST_LEAQ("(%%rsi, %%rcx, 8)"); + + TEST_LEAQ("(,%%rax, 2)"); + TEST_LEAQ("(,%%rbx, 4)"); + TEST_LEAQ("(,%%rcx, 8)"); + + TEST_LEAQ("0x40(,%%rax, 2)"); + TEST_LEAQ("0x40(,%%rbx, 4)"); + TEST_LEAQ("0x40(,%%rcx, 8)"); + + + TEST_LEAQ("-10(%%rcx, %%rcx, 2)"); + TEST_LEAQ("-10(%%rdx, %%rcx, 4)"); + TEST_LEAQ("-10(%%rsi, %%rcx, 8)"); + + TEST_LEAQ("0x4000(%%rcx, %%rcx, 2)"); + TEST_LEAQ("0x4000(%%rdx, %%rcx, 4)"); + TEST_LEAQ("0x4000(%%rsi, %%rcx, 8)"); +#else + /* limited 16 bit addressing test */ + TEST_LEA16("0x4000"); + TEST_LEA16("(%%bx)"); + TEST_LEA16("(%%si)"); + TEST_LEA16("(%%di)"); + TEST_LEA16("0x40(%%bx)"); + TEST_LEA16("0x40(%%si)"); + TEST_LEA16("0x40(%%di)"); + TEST_LEA16("0x4000(%%bx)"); + TEST_LEA16("0x4000(%%si)"); + TEST_LEA16("(%%bx,%%si)"); + TEST_LEA16("(%%bx,%%di)"); + TEST_LEA16("0x40(%%bx,%%si)"); + TEST_LEA16("0x40(%%bx,%%di)"); + TEST_LEA16("0x4000(%%bx,%%si)"); + TEST_LEA16("0x4000(%%bx,%%di)"); +#endif +} + +#define TEST_JCC(JCC, v1, v2)\ +{\ + int res;\ + asm("movl $1, %0\n\t"\ + "cmpl %2, %1\n\t"\ + "j" JCC " 1f\n\t"\ + "movl $0, %0\n\t"\ + "1:\n\t"\ + : "=r" (res)\ + : "r" (v1), "r" (v2));\ + printf("%-10s %d\n", "j" JCC, res);\ +\ + asm("movl $0, %0\n\t"\ + "cmpl %2, %1\n\t"\ + "set" JCC " %b0\n\t"\ + : "=r" (res)\ + : "r" (v1), "r" (v2));\ + printf("%-10s %d\n", "set" JCC, res);\ + if (TEST_CMOV) {\ + long val = i2l(1);\ + long res = i2l(0x12345678);\ +X86_64_ONLY(\ + asm("cmpl %2, %1\n\t"\ + "cmov" JCC "q %3, %0\n\t"\ + : "=r" (res)\ + : "r" (v1), "r" (v2), "m" (val), "0" (res));\ + printf("%-10s R=" FMTLX "\n", "cmov" JCC "q", res);)\ + asm("cmpl %2, %1\n\t"\ + "cmov" JCC "l %k3, %k0\n\t"\ + : "=r" (res)\ + : "r" (v1), "r" (v2), "m" (val), "0" (res));\ + printf("%-10s R=" FMTLX "\n", "cmov" JCC "l", res);\ + asm("cmpl %2, %1\n\t"\ + "cmov" JCC "w %w3, %w0\n\t"\ + : "=r" (res)\ + : "r" (v1), "r" (v2), "r" (1), "0" (res));\ + printf("%-10s R=" FMTLX "\n", "cmov" JCC "w", res);\ + } \ +} + +/* various jump tests */ +void test_jcc(void) +{ + TEST_JCC("ne", 1, 1); + TEST_JCC("ne", 1, 0); + + TEST_JCC("e", 1, 1); + TEST_JCC("e", 1, 0); + + TEST_JCC("l", 1, 1); + TEST_JCC("l", 1, 0); + TEST_JCC("l", 1, -1); + + TEST_JCC("le", 1, 1); + TEST_JCC("le", 1, 0); + TEST_JCC("le", 1, -1); + + TEST_JCC("ge", 1, 1); + TEST_JCC("ge", 1, 0); + TEST_JCC("ge", -1, 1); + + TEST_JCC("g", 1, 1); + TEST_JCC("g", 1, 0); + TEST_JCC("g", 1, -1); + + TEST_JCC("b", 1, 1); + TEST_JCC("b", 1, 0); + TEST_JCC("b", 1, -1); + + TEST_JCC("be", 1, 1); + TEST_JCC("be", 1, 0); + TEST_JCC("be", 1, -1); + + TEST_JCC("ae", 1, 1); + TEST_JCC("ae", 1, 0); + TEST_JCC("ae", 1, -1); + + TEST_JCC("a", 1, 1); + TEST_JCC("a", 1, 0); + TEST_JCC("a", 1, -1); + + + TEST_JCC("p", 1, 1); + TEST_JCC("p", 1, 0); + + TEST_JCC("np", 1, 1); + TEST_JCC("np", 1, 0); + + TEST_JCC("o", 0x7fffffff, 0); + TEST_JCC("o", 0x7fffffff, -1); + + TEST_JCC("no", 0x7fffffff, 0); + TEST_JCC("no", 0x7fffffff, -1); + + TEST_JCC("s", 0, 1); + TEST_JCC("s", 0, -1); + TEST_JCC("s", 0, 0); + + TEST_JCC("ns", 0, 1); + TEST_JCC("ns", 0, -1); + TEST_JCC("ns", 0, 0); +} + +#undef CC_MASK +#ifdef TEST_P4_FLAGS +#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A) +#else +#define CC_MASK (CC_O | CC_C) +#endif + +#define OP mul +#include "test-i386-muldiv.h" + +#define OP imul +#include "test-i386-muldiv.h" + +void test_imulw2(long op0, long op1) +{ + long res, s1, s0, flags; + s0 = op0; + s1 = op1; + res = s0; + flags = 0; + asm volatile ("push %4\n\t" + "popf\n\t" + "imulw %w2, %w0\n\t" + "pushf\n\t" + "pop %1\n\t" + : "=q" (res), "=g" (flags) + : "q" (s1), "0" (res), "1" (flags)); + printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n", + "imulw", s0, s1, res, flags & CC_MASK); +} + +void test_imull2(long op0, long op1) +{ + long res, s1, s0, flags; + s0 = op0; + s1 = op1; + res = s0; + flags = 0; + asm volatile ("push %4\n\t" + "popf\n\t" + "imull %k2, %k0\n\t" + "pushf\n\t" + "pop %1\n\t" + : "=q" (res), "=g" (flags) + : "q" (s1), "0" (res), "1" (flags)); + printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n", + "imull", s0, s1, res, flags & CC_MASK); +} + +#if defined(__x86_64__) +void test_imulq2(long op0, long op1) +{ + long res, s1, s0, flags; + s0 = op0; + s1 = op1; + res = s0; + flags = 0; + asm volatile ("push %4\n\t" + "popf\n\t" + "imulq %2, %0\n\t" + "pushf\n\t" + "pop %1\n\t" + : "=q" (res), "=g" (flags) + : "q" (s1), "0" (res), "1" (flags)); + printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n", + "imulq", s0, s1, res, flags & CC_MASK); +} +#endif + +#define TEST_IMUL_IM(size, rsize, op0, op1)\ +{\ + long res, flags, s1;\ + flags = 0;\ + res = 0;\ + s1 = op1;\ + asm volatile ("push %3\n\t"\ + "popf\n\t"\ + "imul" size " $" #op0 ", %" rsize "2, %" rsize "0\n\t" \ + "pushf\n\t"\ + "pop %1\n\t"\ + : "=r" (res), "=g" (flags)\ + : "r" (s1), "1" (flags), "0" (res));\ + printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",\ + "imul" size " im", (long)op0, (long)op1, res, flags & CC_MASK);\ +} + + +#undef CC_MASK +#define CC_MASK (0) + +#define OP div +#include "test-i386-muldiv.h" + +#define OP idiv +#include "test-i386-muldiv.h" + +void test_mul(void) +{ + test_imulb(0x1234561d, 4); + test_imulb(3, -4); + test_imulb(0x80, 0x80); + test_imulb(0x10, 0x10); + + test_imulw(0, 0x1234001d, 45); + test_imulw(0, 23, -45); + test_imulw(0, 0x8000, 0x8000); + test_imulw(0, 0x100, 0x100); + + test_imull(0, 0x1234001d, 45); + test_imull(0, 23, -45); + test_imull(0, 0x80000000, 0x80000000); + test_imull(0, 0x10000, 0x10000); + + test_mulb(0x1234561d, 4); + test_mulb(3, -4); + test_mulb(0x80, 0x80); + test_mulb(0x10, 0x10); + + test_mulw(0, 0x1234001d, 45); + test_mulw(0, 23, -45); + test_mulw(0, 0x8000, 0x8000); + test_mulw(0, 0x100, 0x100); + + test_mull(0, 0x1234001d, 45); + test_mull(0, 23, -45); + test_mull(0, 0x80000000, 0x80000000); + test_mull(0, 0x10000, 0x10000); + + test_imulw2(0x1234001d, 45); + test_imulw2(23, -45); + test_imulw2(0x8000, 0x8000); + test_imulw2(0x100, 0x100); + + test_imull2(0x1234001d, 45); + test_imull2(23, -45); + test_imull2(0x80000000, 0x80000000); + test_imull2(0x10000, 0x10000); + + TEST_IMUL_IM("w", "w", 45, 0x1234); + TEST_IMUL_IM("w", "w", -45, 23); + TEST_IMUL_IM("w", "w", 0x8000, 0x80000000); + TEST_IMUL_IM("w", "w", 0x7fff, 0x1000); + + TEST_IMUL_IM("l", "k", 45, 0x1234); + TEST_IMUL_IM("l", "k", -45, 23); + TEST_IMUL_IM("l", "k", 0x8000, 0x80000000); + TEST_IMUL_IM("l", "k", 0x7fff, 0x1000); + + test_idivb(0x12341678, 0x127e); + test_idivb(0x43210123, -5); + test_idivb(0x12340004, -1); + + test_idivw(0, 0x12345678, 12347); + test_idivw(0, -23223, -45); + test_idivw(0, 0x12348000, -1); + test_idivw(0x12343, 0x12345678, 0x81238567); + + test_idivl(0, 0x12345678, 12347); + test_idivl(0, -233223, -45); + test_idivl(0, 0x80000000, -1); + test_idivl(0x12343, 0x12345678, 0x81234567); + + test_divb(0x12341678, 0x127e); + test_divb(0x43210123, -5); + test_divb(0x12340004, -1); + + test_divw(0, 0x12345678, 12347); + test_divw(0, -23223, -45); + test_divw(0, 0x12348000, -1); + test_divw(0x12343, 0x12345678, 0x81238567); + + test_divl(0, 0x12345678, 12347); + test_divl(0, -233223, -45); + test_divl(0, 0x80000000, -1); + test_divl(0x12343, 0x12345678, 0x81234567); + +#if defined(__x86_64__) + test_imulq(0, 0x1234001d1234001d, 45); + test_imulq(0, 23, -45); + test_imulq(0, 0x8000000000000000, 0x8000000000000000); + test_imulq(0, 0x100000000, 0x100000000); + + test_mulq(0, 0x1234001d1234001d, 45); + test_mulq(0, 23, -45); + test_mulq(0, 0x8000000000000000, 0x8000000000000000); + test_mulq(0, 0x100000000, 0x100000000); + + test_imulq2(0x1234001d1234001d, 45); + test_imulq2(23, -45); + test_imulq2(0x8000000000000000, 0x8000000000000000); + test_imulq2(0x100000000, 0x100000000); + + TEST_IMUL_IM("q", "", 45, 0x12341234); + TEST_IMUL_IM("q", "", -45, 23); + TEST_IMUL_IM("q", "", 0x8000, 0x8000000000000000); + TEST_IMUL_IM("q", "", 0x7fff, 0x10000000); + + test_idivq(0, 0x12345678abcdef, 12347); + test_idivq(0, -233223, -45); + test_idivq(0, 0x8000000000000000, -1); + test_idivq(0x12343, 0x12345678, 0x81234567); + + test_divq(0, 0x12345678abcdef, 12347); + test_divq(0, -233223, -45); + test_divq(0, 0x8000000000000000, -1); + test_divq(0x12343, 0x12345678, 0x81234567); +#endif +} + +#define TEST_BSX(op, size, op0)\ +{\ + long res, val, resz;\ + val = op0;\ + asm("xor %1, %1\n"\ + "mov $0x12345678, %0\n"\ + #op " %" size "2, %" size "0 ; setz %b1" \ + : "=r" (res), "=q" (resz)\ + : "g" (val));\ + printf("%-10s A=" FMTLX " R=" FMTLX " %ld\n", #op, val, res, resz);\ +} + +void test_bsx(void) +{ + TEST_BSX(bsrw, "w", 0); + TEST_BSX(bsrw, "w", 0x12340128); + TEST_BSX(bsfw, "w", 0); + TEST_BSX(bsfw, "w", 0x12340128); + TEST_BSX(bsrl, "k", 0); + TEST_BSX(bsrl, "k", 0x00340128); + TEST_BSX(bsfl, "k", 0); + TEST_BSX(bsfl, "k", 0x00340128); +#if defined(__x86_64__) + TEST_BSX(bsrq, "", 0); + TEST_BSX(bsrq, "", 0x003401281234); + TEST_BSX(bsfq, "", 0); + TEST_BSX(bsfq, "", 0x003401281234); +#endif +} + +/**********************************************/ + +union float64u { + double d; + uint64_t l; +}; + +union float64u q_nan = { .l = 0xFFF8000000000000 }; +union float64u s_nan = { .l = 0xFFF0000000000000 }; + +void test_fops(double a, double b) +{ + printf("a=%f b=%f a+b=%f\n", a, b, a + b); + printf("a=%f b=%f a-b=%f\n", a, b, a - b); + printf("a=%f b=%f a*b=%f\n", a, b, a * b); + printf("a=%f b=%f a/b=%f\n", a, b, a / b); + printf("a=%f b=%f fmod(a, b)=%f\n", a, b, fmod(a, b)); + printf("a=%f sqrt(a)=%f\n", a, sqrt(a)); + printf("a=%f sin(a)=%f\n", a, sin(a)); + printf("a=%f cos(a)=%f\n", a, cos(a)); + printf("a=%f tan(a)=%f\n", a, tan(a)); + printf("a=%f log(a)=%f\n", a, log(a)); + printf("a=%f exp(a)=%f\n", a, exp(a)); + printf("a=%f b=%f atan2(a, b)=%f\n", a, b, atan2(a, b)); + /* just to test some op combining */ + printf("a=%f asin(sin(a))=%f\n", a, asin(sin(a))); + printf("a=%f acos(cos(a))=%f\n", a, acos(cos(a))); + printf("a=%f atan(tan(a))=%f\n", a, atan(tan(a))); + +} + +void fpu_clear_exceptions(void) +{ + struct __attribute__((packed)) { + uint16_t fpuc; + uint16_t dummy1; + uint16_t fpus; + uint16_t dummy2; + uint16_t fptag; + uint16_t dummy3; + uint32_t ignored[4]; + long double fpregs[8]; + } float_env32; + + asm volatile ("fnstenv %0\n" : : "m" (float_env32)); + float_env32.fpus &= ~0x7f; + asm volatile ("fldenv %0\n" : : "m" (float_env32)); +} + +/* XXX: display exception bits when supported */ +#define FPUS_EMASK 0x0000 +//#define FPUS_EMASK 0x007f + +void test_fcmp(double a, double b) +{ + long eflags, fpus; + + fpu_clear_exceptions(); + asm("fcom %2\n" + "fstsw %%ax\n" + : "=a" (fpus) + : "t" (a), "u" (b)); + printf("fcom(%f %f)=%04lx \n", + a, b, fpus & (0x4500 | FPUS_EMASK)); + fpu_clear_exceptions(); + asm("fucom %2\n" + "fstsw %%ax\n" + : "=a" (fpus) + : "t" (a), "u" (b)); + printf("fucom(%f %f)=%04lx\n", + a, b, fpus & (0x4500 | FPUS_EMASK)); + if (TEST_FCOMI) { + /* test f(u)comi instruction */ + fpu_clear_exceptions(); + asm("fcomi %3, %2\n" + "fstsw %%ax\n" + "pushf\n" + "pop %0\n" + : "=r" (eflags), "=a" (fpus) + : "t" (a), "u" (b)); + printf("fcomi(%f %f)=%04lx %02lx\n", + a, b, fpus & FPUS_EMASK, eflags & (CC_Z | CC_P | CC_C)); + fpu_clear_exceptions(); + asm("fucomi %3, %2\n" + "fstsw %%ax\n" + "pushf\n" + "pop %0\n" + : "=r" (eflags), "=a" (fpus) + : "t" (a), "u" (b)); + printf("fucomi(%f %f)=%04lx %02lx\n", + a, b, fpus & FPUS_EMASK, eflags & (CC_Z | CC_P | CC_C)); + } + fpu_clear_exceptions(); +} + +void test_fcvt(double a) +{ + float fa; + long double la; + int16_t fpuc; + int i; + int64_t lla; + int ia; + int16_t wa; + double ra; + + fa = a; + la = a; + printf("(float)%f = %f\n", a, fa); + printf("(long double)%f = %Lf\n", a, la); + printf("a=" FMT64X "\n", *(uint64_t *)&a); + printf("la=" FMT64X " %04x\n", *(uint64_t *)&la, + *(unsigned short *)((char *)(&la) + 8)); + + /* test all roundings */ + asm volatile ("fstcw %0" : "=m" (fpuc)); + for(i=0;i<4;i++) { + asm volatile ("fldcw %0" : : "m" ((fpuc & ~0x0c00) | (i << 10))); + asm volatile ("fist %0" : "=m" (wa) : "t" (a)); + asm volatile ("fistl %0" : "=m" (ia) : "t" (a)); + asm volatile ("fistpll %0" : "=m" (lla) : "t" (a) : "st"); + asm volatile ("frndint ; fstl %0" : "=m" (ra) : "t" (a)); + asm volatile ("fldcw %0" : : "m" (fpuc)); + printf("(short)a = %d\n", wa); + printf("(int)a = %d\n", ia); + printf("(int64_t)a = " FMT64X "\n", lla); + printf("rint(a) = %f\n", ra); + } +} + +#define TEST(N) \ + asm("fld" #N : "=t" (a)); \ + printf("fld" #N "= %f\n", a); + +void test_fconst(void) +{ + double a; + TEST(1); + TEST(l2t); + TEST(l2e); + TEST(pi); + TEST(lg2); + TEST(ln2); + TEST(z); +} + +void test_fbcd(double a) +{ + unsigned short bcd[5]; + double b; + + asm("fbstp %0" : "=m" (bcd[0]) : "t" (a) : "st"); + asm("fbld %1" : "=t" (b) : "m" (bcd[0])); + printf("a=%f bcd=%04x%04x%04x%04x%04x b=%f\n", + a, bcd[4], bcd[3], bcd[2], bcd[1], bcd[0], b); +} + +#define TEST_ENV(env, save, restore)\ +{\ + memset((env), 0xaa, sizeof(*(env)));\ + for(i=0;i<5;i++)\ + asm volatile ("fldl %0" : : "m" (dtab[i]));\ + asm volatile (save " %0\n" : : "m" (*(env)));\ + asm volatile (restore " %0\n": : "m" (*(env)));\ + for(i=0;i<5;i++)\ + asm volatile ("fstpl %0" : "=m" (rtab[i]));\ + for(i=0;i<5;i++)\ + printf("res[%d]=%f\n", i, rtab[i]);\ + printf("fpuc=%04x fpus=%04x fptag=%04x\n",\ + (env)->fpuc,\ + (env)->fpus & 0xff00,\ + (env)->fptag);\ +} + +void test_fenv(void) +{ + struct __attribute__((packed)) { + uint16_t fpuc; + uint16_t dummy1; + uint16_t fpus; + uint16_t dummy2; + uint16_t fptag; + uint16_t dummy3; + uint32_t ignored[4]; + long double fpregs[8]; + } float_env32; + struct __attribute__((packed)) { + uint16_t fpuc; + uint16_t fpus; + uint16_t fptag; + uint16_t ignored[4]; + long double fpregs[8]; + } float_env16; + double dtab[8]; + double rtab[8]; + int i; + + for(i=0;i<8;i++) + dtab[i] = i + 1; + + TEST_ENV(&float_env16, "data16 fnstenv", "data16 fldenv"); + TEST_ENV(&float_env16, "data16 fnsave", "data16 frstor"); + TEST_ENV(&float_env32, "fnstenv", "fldenv"); + TEST_ENV(&float_env32, "fnsave", "frstor"); + + /* test for ffree */ + for(i=0;i<5;i++) + asm volatile ("fldl %0" : : "m" (dtab[i])); + asm volatile("ffree %st(2)"); + asm volatile ("fnstenv %0\n" : : "m" (float_env32)); + asm volatile ("fninit"); + printf("fptag=%04x\n", float_env32.fptag); +} + + +#define TEST_FCMOV(a, b, eflags, CC)\ +{\ + double res;\ + asm("push %3\n"\ + "popf\n"\ + "fcmov" CC " %2, %0\n"\ + : "=t" (res)\ + : "0" (a), "u" (b), "g" (eflags));\ + printf("fcmov%s eflags=0x%04lx-> %f\n", \ + CC, (long)eflags, res);\ +} + +void test_fcmov(void) +{ + double a, b; + long eflags, i; + + a = 1.0; + b = 2.0; + for(i = 0; i < 4; i++) { + eflags = 0; + if (i & 1) + eflags |= CC_C; + if (i & 2) + eflags |= CC_Z; + TEST_FCMOV(a, b, eflags, "b"); + TEST_FCMOV(a, b, eflags, "e"); + TEST_FCMOV(a, b, eflags, "be"); + TEST_FCMOV(a, b, eflags, "nb"); + TEST_FCMOV(a, b, eflags, "ne"); + TEST_FCMOV(a, b, eflags, "nbe"); + } + TEST_FCMOV(a, b, 0, "u"); + TEST_FCMOV(a, b, CC_P, "u"); + TEST_FCMOV(a, b, 0, "nu"); + TEST_FCMOV(a, b, CC_P, "nu"); +} + +void test_floats(void) +{ + test_fops(2, 3); + test_fops(1.4, -5); + test_fcmp(2, -1); + test_fcmp(2, 2); + test_fcmp(2, 3); + test_fcmp(2, q_nan.d); + test_fcmp(q_nan.d, -1); + test_fcvt(0.5); + test_fcvt(-0.5); + test_fcvt(1.0/7.0); + test_fcvt(-1.0/9.0); + test_fcvt(32768); + test_fcvt(-1e20); + test_fconst(); + test_fbcd(1234567890123456); + test_fbcd(-123451234567890); + test_fenv(); + if (TEST_CMOV) { + test_fcmov(); + } +} + +/**********************************************/ +#if !defined(__x86_64__) + +#define TEST_BCD(op, op0, cc_in, cc_mask)\ +{\ + int res, flags;\ + res = op0;\ + flags = cc_in;\ + asm ("push %3\n\t"\ + "popf\n\t"\ + #op "\n\t"\ + "pushf\n\t"\ + "pop %1\n\t"\ + : "=a" (res), "=g" (flags)\ + : "0" (res), "1" (flags));\ + printf("%-10s A=%08x R=%08x CCIN=%04x CC=%04x\n",\ + #op, op0, res, cc_in, flags & cc_mask);\ +} + +void test_bcd(void) +{ + TEST_BCD(daa, 0x12340503, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(daa, 0x12340506, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(daa, 0x12340507, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(daa, 0x12340559, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(daa, 0x12340560, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(daa, 0x1234059f, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(daa, 0x123405a0, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(daa, 0x12340503, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(daa, 0x12340506, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(daa, 0x12340503, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(daa, 0x12340506, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(daa, 0x12340503, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(daa, 0x12340506, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + + TEST_BCD(das, 0x12340503, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(das, 0x12340506, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(das, 0x12340507, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(das, 0x12340559, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(das, 0x12340560, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(das, 0x1234059f, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(das, 0x123405a0, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(das, 0x12340503, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(das, 0x12340506, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(das, 0x12340503, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(das, 0x12340506, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(das, 0x12340503, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + TEST_BCD(das, 0x12340506, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A)); + + TEST_BCD(aaa, 0x12340205, CC_A, (CC_C | CC_A)); + TEST_BCD(aaa, 0x12340306, CC_A, (CC_C | CC_A)); + TEST_BCD(aaa, 0x1234040a, CC_A, (CC_C | CC_A)); + TEST_BCD(aaa, 0x123405fa, CC_A, (CC_C | CC_A)); + TEST_BCD(aaa, 0x12340205, 0, (CC_C | CC_A)); + TEST_BCD(aaa, 0x12340306, 0, (CC_C | CC_A)); + TEST_BCD(aaa, 0x1234040a, 0, (CC_C | CC_A)); + TEST_BCD(aaa, 0x123405fa, 0, (CC_C | CC_A)); + + TEST_BCD(aas, 0x12340205, CC_A, (CC_C | CC_A)); + TEST_BCD(aas, 0x12340306, CC_A, (CC_C | CC_A)); + TEST_BCD(aas, 0x1234040a, CC_A, (CC_C | CC_A)); + TEST_BCD(aas, 0x123405fa, CC_A, (CC_C | CC_A)); + TEST_BCD(aas, 0x12340205, 0, (CC_C | CC_A)); + TEST_BCD(aas, 0x12340306, 0, (CC_C | CC_A)); + TEST_BCD(aas, 0x1234040a, 0, (CC_C | CC_A)); + TEST_BCD(aas, 0x123405fa, 0, (CC_C | CC_A)); + + TEST_BCD(aam, 0x12340547, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)); + TEST_BCD(aad, 0x12340407, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)); +} +#endif + +#define TEST_XCHG(op, size, opconst)\ +{\ + long op0, op1;\ + op0 = i2l(0x12345678);\ + op1 = i2l(0xfbca7654);\ + asm(#op " %" size "0, %" size "1" \ + : "=q" (op0), opconst (op1) \ + : "0" (op0), "1" (op1));\ + printf("%-10s A=" FMTLX " B=" FMTLX "\n",\ + #op, op0, op1);\ +} + +#define TEST_CMPXCHG(op, size, opconst, eax)\ +{\ + long op0, op1, op2;\ + op0 = i2l(0x12345678);\ + op1 = i2l(0xfbca7654);\ + op2 = i2l(eax);\ + asm(#op " %" size "0, %" size "1" \ + : "=q" (op0), opconst (op1) \ + : "0" (op0), "1" (op1), "a" (op2));\ + printf("%-10s EAX=" FMTLX " A=" FMTLX " C=" FMTLX "\n",\ + #op, op2, op0, op1);\ +} + +void test_xchg(void) +{ +#if defined(__x86_64__) + TEST_XCHG(xchgq, "", "=q"); +#endif + TEST_XCHG(xchgl, "k", "=q"); + TEST_XCHG(xchgw, "w", "=q"); + TEST_XCHG(xchgb, "b", "=q"); + +#if defined(__x86_64__) + TEST_XCHG(xchgq, "", "=m"); +#endif + TEST_XCHG(xchgl, "k", "=m"); + TEST_XCHG(xchgw, "w", "=m"); + TEST_XCHG(xchgb, "b", "=m"); + +#if defined(__x86_64__) + TEST_XCHG(xaddq, "", "=q"); +#endif + TEST_XCHG(xaddl, "k", "=q"); + TEST_XCHG(xaddw, "w", "=q"); + TEST_XCHG(xaddb, "b", "=q"); + + { + int res; + res = 0x12345678; + asm("xaddl %1, %0" : "=r" (res) : "0" (res)); + printf("xaddl same res=%08x\n", res); + } + +#if defined(__x86_64__) + TEST_XCHG(xaddq, "", "=m"); +#endif + TEST_XCHG(xaddl, "k", "=m"); + TEST_XCHG(xaddw, "w", "=m"); + TEST_XCHG(xaddb, "b", "=m"); + +#if defined(__x86_64__) + TEST_CMPXCHG(cmpxchgq, "", "=q", 0xfbca7654); +#endif + TEST_CMPXCHG(cmpxchgl, "k", "=q", 0xfbca7654); + TEST_CMPXCHG(cmpxchgw, "w", "=q", 0xfbca7654); + TEST_CMPXCHG(cmpxchgb, "b", "=q", 0xfbca7654); + +#if defined(__x86_64__) + TEST_CMPXCHG(cmpxchgq, "", "=q", 0xfffefdfc); +#endif + TEST_CMPXCHG(cmpxchgl, "k", "=q", 0xfffefdfc); + TEST_CMPXCHG(cmpxchgw, "w", "=q", 0xfffefdfc); + TEST_CMPXCHG(cmpxchgb, "b", "=q", 0xfffefdfc); + +#if defined(__x86_64__) + TEST_CMPXCHG(cmpxchgq, "", "=m", 0xfbca7654); +#endif + TEST_CMPXCHG(cmpxchgl, "k", "=m", 0xfbca7654); + TEST_CMPXCHG(cmpxchgw, "w", "=m", 0xfbca7654); + TEST_CMPXCHG(cmpxchgb, "b", "=m", 0xfbca7654); + +#if defined(__x86_64__) + TEST_CMPXCHG(cmpxchgq, "", "=m", 0xfffefdfc); +#endif + TEST_CMPXCHG(cmpxchgl, "k", "=m", 0xfffefdfc); + TEST_CMPXCHG(cmpxchgw, "w", "=m", 0xfffefdfc); + TEST_CMPXCHG(cmpxchgb, "b", "=m", 0xfffefdfc); + + { + uint64_t op0, op1, op2; + long i, eflags; + + for(i = 0; i < 2; i++) { + op0 = 0x123456789abcd; + if (i == 0) + op1 = 0xfbca765423456; + else + op1 = op0; + op2 = 0x6532432432434; + asm("cmpxchg8b %1\n" + "pushf\n" + "pop %2\n" + : "=A" (op0), "=m" (op1), "=g" (eflags) + : "0" (op0), "m" (op1), "b" ((int)op2), "c" ((int)(op2 >> 32))); + printf("cmpxchg8b: op0=" FMT64X " op1=" FMT64X " CC=%02lx\n", + op0, op1, eflags & CC_Z); + } + } +} + +#ifdef TEST_SEGS +/**********************************************/ +/* segmentation tests */ + +#include +#include +#include + +_syscall3(int, modify_ldt, int, func, void *, ptr, unsigned long, bytecount) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 66) +#define modify_ldt_ldt_s user_desc +#endif + +#define MK_SEL(n) (((n) << 3) | 7) + +uint8_t seg_data1[4096]; +uint8_t seg_data2[4096]; + +#define TEST_LR(op, size, seg, mask)\ +{\ + int res, res2;\ + res = 0x12345678;\ + asm (op " %" size "2, %" size "0\n" \ + "movl $0, %1\n"\ + "jnz 1f\n"\ + "movl $1, %1\n"\ + "1:\n"\ + : "=r" (res), "=r" (res2) : "m" (seg), "0" (res));\ + printf(op ": Z=%d %08x\n", res2, res & ~(mask));\ +} + +/* NOTE: we use Linux modify_ldt syscall */ +void test_segs(void) +{ + struct modify_ldt_ldt_s ldt; + long long ldt_table[3]; + int res, res2; + char tmp; + struct { + uint32_t offset; + uint16_t seg; + } __attribute__((packed)) segoff; + + ldt.entry_number = 1; + ldt.base_addr = (unsigned long)&seg_data1; + ldt.limit = (sizeof(seg_data1) + 0xfff) >> 12; + ldt.seg_32bit = 1; + ldt.contents = MODIFY_LDT_CONTENTS_DATA; + ldt.read_exec_only = 0; + ldt.limit_in_pages = 1; + ldt.seg_not_present = 0; + ldt.useable = 1; + modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */ + + ldt.entry_number = 2; + ldt.base_addr = (unsigned long)&seg_data2; + ldt.limit = (sizeof(seg_data2) + 0xfff) >> 12; + ldt.seg_32bit = 1; + ldt.contents = MODIFY_LDT_CONTENTS_DATA; + ldt.read_exec_only = 0; + ldt.limit_in_pages = 1; + ldt.seg_not_present = 0; + ldt.useable = 1; + modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */ + + modify_ldt(0, &ldt_table, sizeof(ldt_table)); /* read ldt entries */ +#if 0 + { + int i; + for(i=0;i<3;i++) + printf("%d: %016Lx\n", i, ldt_table[i]); + } +#endif + /* do some tests with fs or gs */ + asm volatile ("movl %0, %%fs" : : "r" (MK_SEL(1))); + + seg_data1[1] = 0xaa; + seg_data2[1] = 0x55; + + asm volatile ("fs movzbl 0x1, %0" : "=r" (res)); + printf("FS[1] = %02x\n", res); + + asm volatile ("pushl %%gs\n" + "movl %1, %%gs\n" + "gs movzbl 0x1, %0\n" + "popl %%gs\n" + : "=r" (res) + : "r" (MK_SEL(2))); + printf("GS[1] = %02x\n", res); + + /* tests with ds/ss (implicit segment case) */ + tmp = 0xa5; + asm volatile ("pushl %%ebp\n\t" + "pushl %%ds\n\t" + "movl %2, %%ds\n\t" + "movl %3, %%ebp\n\t" + "movzbl 0x1, %0\n\t" + "movzbl (%%ebp), %1\n\t" + "popl %%ds\n\t" + "popl %%ebp\n\t" + : "=r" (res), "=r" (res2) + : "r" (MK_SEL(1)), "r" (&tmp)); + printf("DS[1] = %02x\n", res); + printf("SS[tmp] = %02x\n", res2); + + segoff.seg = MK_SEL(2); + segoff.offset = 0xabcdef12; + asm volatile("lfs %2, %0\n\t" + "movl %%fs, %1\n\t" + : "=r" (res), "=g" (res2) + : "m" (segoff)); + printf("FS:reg = %04x:%08x\n", res2, res); + + TEST_LR("larw", "w", MK_SEL(2), 0x0100); + TEST_LR("larl", "", MK_SEL(2), 0x0100); + TEST_LR("lslw", "w", MK_SEL(2), 0); + TEST_LR("lsll", "", MK_SEL(2), 0); + + TEST_LR("larw", "w", 0xfff8, 0); + TEST_LR("larl", "", 0xfff8, 0); + TEST_LR("lslw", "w", 0xfff8, 0); + TEST_LR("lsll", "", 0xfff8, 0); +} + +/* 16 bit code test */ +extern char code16_start, code16_end; +extern char code16_func1; +extern char code16_func2; +extern char code16_func3; + +void test_code16(void) +{ + struct modify_ldt_ldt_s ldt; + int res, res2; + + /* build a code segment */ + ldt.entry_number = 1; + ldt.base_addr = (unsigned long)&code16_start; + ldt.limit = &code16_end - &code16_start; + ldt.seg_32bit = 0; + ldt.contents = MODIFY_LDT_CONTENTS_CODE; + ldt.read_exec_only = 0; + ldt.limit_in_pages = 0; + ldt.seg_not_present = 0; + ldt.useable = 1; + modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */ + + /* call the first function */ + asm volatile ("lcall %1, %2" + : "=a" (res) + : "i" (MK_SEL(1)), "i" (&code16_func1): "memory", "cc"); + printf("func1() = 0x%08x\n", res); + asm volatile ("lcall %2, %3" + : "=a" (res), "=c" (res2) + : "i" (MK_SEL(1)), "i" (&code16_func2): "memory", "cc"); + printf("func2() = 0x%08x spdec=%d\n", res, res2); + asm volatile ("lcall %1, %2" + : "=a" (res) + : "i" (MK_SEL(1)), "i" (&code16_func3): "memory", "cc"); + printf("func3() = 0x%08x\n", res); +} +#endif + +#if defined(__x86_64__) +asm(".globl func_lret\n" + "func_lret:\n" + "movl $0x87654641, %eax\n" + "lretq\n"); +#else +asm(".globl func_lret\n" + "func_lret:\n" + "movl $0x87654321, %eax\n" + "lret\n" + + ".globl func_iret\n" + "func_iret:\n" + "movl $0xabcd4321, %eax\n" + "iret\n"); +#endif + +extern char func_lret; +extern char func_iret; + +void test_misc(void) +{ + char table[256]; + long res, i; + + for(i=0;i<256;i++) table[i] = 256 - i; + res = 0x12345678; + asm ("xlat" : "=a" (res) : "b" (table), "0" (res)); + printf("xlat: EAX=" FMTLX "\n", res); + +#if defined(__x86_64__) + { + static struct __attribute__((packed)) { + uint32_t offset; + uint16_t seg; + } desc; + long cs_sel; + + asm volatile ("mov %%cs, %0" : "=r" (cs_sel)); + + asm volatile ("push %1\n" + "call func_lret\n" + : "=a" (res) + : "r" (cs_sel) : "memory", "cc"); + printf("func_lret=" FMTLX "\n", res); + + /* NOTE: we assume that &func_lret < 4GB */ + desc.offset = (long)&func_lret; + desc.seg = cs_sel; + + asm volatile ("xor %%rax, %%rax\n" + "rex64 lcall %1\n" + : "=a" (res) + : "m" (desc) + : "memory", "cc"); + printf("func_lret2=" FMTLX "\n", res); + + asm volatile ("push %2\n" + "mov $ 1f, %%rax\n" + "push %%rax\n" + "ljmp %1\n" + "1:\n" + : "=a" (res) + : "m" (desc), "b" (cs_sel) + : "memory", "cc"); + printf("func_lret3=" FMTLX "\n", res); + } +#else + asm volatile ("push %%cs ; call %1" + : "=a" (res) + : "m" (func_lret): "memory", "cc"); + printf("func_lret=" FMTLX "\n", res); + + asm volatile ("pushf ; push %%cs ; call %1" + : "=a" (res) + : "m" (func_iret): "memory", "cc"); + printf("func_iret=" FMTLX "\n", res); +#endif + +#if defined(__x86_64__) + /* specific popl test */ + asm volatile ("push $12345432 ; push $0x9abcdef ; pop (%%rsp) ; pop %0" + : "=g" (res)); + printf("popl esp=" FMTLX "\n", res); +#else + /* specific popl test */ + asm volatile ("pushl $12345432 ; pushl $0x9abcdef ; popl (%%esp) ; popl %0" + : "=g" (res)); + printf("popl esp=" FMTLX "\n", res); + + /* specific popw test */ + asm volatile ("pushl $12345432 ; pushl $0x9abcdef ; popw (%%esp) ; addl $2, %%esp ; popl %0" + : "=g" (res)); + printf("popw esp=" FMTLX "\n", res); +#endif +} + +uint8_t str_buffer[4096]; + +#define TEST_STRING1(OP, size, DF, REP)\ +{\ + long esi, edi, eax, ecx, eflags;\ +\ + esi = (long)(str_buffer + sizeof(str_buffer) / 2);\ + edi = (long)(str_buffer + sizeof(str_buffer) / 2) + 16;\ + eax = i2l(0x12345678);\ + ecx = 17;\ +\ + asm volatile ("push $0\n\t"\ + "popf\n\t"\ + DF "\n\t"\ + REP #OP size "\n\t"\ + "cld\n\t"\ + "pushf\n\t"\ + "pop %4\n\t"\ + : "=S" (esi), "=D" (edi), "=a" (eax), "=c" (ecx), "=g" (eflags)\ + : "0" (esi), "1" (edi), "2" (eax), "3" (ecx));\ + printf("%-10s ESI=" FMTLX " EDI=" FMTLX " EAX=" FMTLX " ECX=" FMTLX " EFL=%04x\n",\ + REP #OP size, esi, edi, eax, ecx,\ + (int)(eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)));\ +} + +#define TEST_STRING(OP, REP)\ + TEST_STRING1(OP, "b", "", REP);\ + TEST_STRING1(OP, "w", "", REP);\ + TEST_STRING1(OP, "l", "", REP);\ + X86_64_ONLY(TEST_STRING1(OP, "q", "", REP));\ + TEST_STRING1(OP, "b", "std", REP);\ + TEST_STRING1(OP, "w", "std", REP);\ + TEST_STRING1(OP, "l", "std", REP);\ + X86_64_ONLY(TEST_STRING1(OP, "q", "std", REP)) + +void test_string(void) +{ + int i; + for(i = 0;i < sizeof(str_buffer); i++) + str_buffer[i] = i + 0x56; + TEST_STRING(stos, ""); + TEST_STRING(stos, "rep "); + TEST_STRING(lods, ""); /* to verify stos */ + TEST_STRING(lods, "rep "); + TEST_STRING(movs, ""); + TEST_STRING(movs, "rep "); + TEST_STRING(lods, ""); /* to verify stos */ + + /* XXX: better tests */ + TEST_STRING(scas, ""); + TEST_STRING(scas, "repz "); + TEST_STRING(scas, "repnz "); + TEST_STRING(cmps, ""); + TEST_STRING(cmps, "repz "); + TEST_STRING(cmps, "repnz "); +} + +#ifdef TEST_VM86 +/* VM86 test */ + +static inline void set_bit(uint8_t *a, unsigned int bit) +{ + a[bit / 8] |= (1 << (bit % 8)); +} + +static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg) +{ + return (uint8_t *)((seg << 4) + (reg & 0xffff)); +} + +static inline void pushw(struct vm86_regs *r, int val) +{ + r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff); + *(uint16_t *)seg_to_linear(r->ss, r->esp) = val; +} + +#undef __syscall_return +#define __syscall_return(type, res) \ +do { \ + return (type) (res); \ +} while (0) + +_syscall2(int, vm86, int, func, struct vm86plus_struct *, v86) + +extern char vm86_code_start; +extern char vm86_code_end; + +#define VM86_CODE_CS 0x100 +#define VM86_CODE_IP 0x100 + +void test_vm86(void) +{ + struct vm86plus_struct ctx; + struct vm86_regs *r; + uint8_t *vm86_mem; + int seg, ret; + + vm86_mem = mmap((void *)0x00000000, 0x110000, + PROT_WRITE | PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); + if (vm86_mem == MAP_FAILED) { + printf("ERROR: could not map vm86 memory"); + return; + } + memset(&ctx, 0, sizeof(ctx)); + + /* init basic registers */ + r = &ctx.regs; + r->eip = VM86_CODE_IP; + r->esp = 0xfffe; + seg = VM86_CODE_CS; + r->cs = seg; + r->ss = seg; + r->ds = seg; + r->es = seg; + r->fs = seg; + r->gs = seg; + r->eflags = VIF_MASK; + + /* move code to proper address. We use the same layout as a .com + dos program. */ + memcpy(vm86_mem + (VM86_CODE_CS << 4) + VM86_CODE_IP, + &vm86_code_start, &vm86_code_end - &vm86_code_start); + + /* mark int 0x21 as being emulated */ + set_bit((uint8_t *)&ctx.int_revectored, 0x21); + + for(;;) { + ret = vm86(VM86_ENTER, &ctx); + switch(VM86_TYPE(ret)) { + case VM86_INTx: + { + int int_num, ah, v; + + int_num = VM86_ARG(ret); + if (int_num != 0x21) + goto unknown_int; + ah = (r->eax >> 8) & 0xff; + switch(ah) { + case 0x00: /* exit */ + goto the_end; + case 0x02: /* write char */ + { + uint8_t c = r->edx; + putchar(c); + } + break; + case 0x09: /* write string */ + { + uint8_t c, *ptr; + ptr = seg_to_linear(r->ds, r->edx); + for(;;) { + c = *ptr++; + if (c == '$') + break; + putchar(c); + } + r->eax = (r->eax & ~0xff) | '$'; + } + break; + case 0xff: /* extension: write eflags number in edx */ + v = (int)r->edx; +#ifndef LINUX_VM86_IOPL_FIX + v &= ~0x3000; +#endif + printf("%08x\n", v); + break; + default: + unknown_int: + printf("unsupported int 0x%02x\n", int_num); + goto the_end; + } + } + break; + case VM86_SIGNAL: + /* a signal came, we just ignore that */ + break; + case VM86_STI: + break; + default: + printf("ERROR: unhandled vm86 return code (0x%x)\n", ret); + goto the_end; + } + } + the_end: + printf("VM86 end\n"); + munmap(vm86_mem, 0x110000); +} +#endif + +/* exception tests */ +#if defined(__i386__) && !defined(REG_EAX) +#define REG_EAX EAX +#define REG_EBX EBX +#define REG_ECX ECX +#define REG_EDX EDX +#define REG_ESI ESI +#define REG_EDI EDI +#define REG_EBP EBP +#define REG_ESP ESP +#define REG_EIP EIP +#define REG_EFL EFL +#define REG_TRAPNO TRAPNO +#define REG_ERR ERR +#endif + +#if defined(__x86_64__) +#define REG_EIP REG_RIP +#endif + +jmp_buf jmp_env; +int v1; +int tab[2]; + +void sig_handler(int sig, siginfo_t *info, void *puc) +{ + struct ucontext *uc = puc; + + printf("si_signo=%d si_errno=%d si_code=%d", + info->si_signo, info->si_errno, info->si_code); + printf(" si_addr=0x%08lx", + (unsigned long)info->si_addr); + printf("\n"); + + printf("trapno=" FMTLX " err=" FMTLX, + (long)uc->uc_mcontext.gregs[REG_TRAPNO], + (long)uc->uc_mcontext.gregs[REG_ERR]); + printf(" EIP=" FMTLX, (long)uc->uc_mcontext.gregs[REG_EIP]); + printf("\n"); + longjmp(jmp_env, 1); +} + +void test_exceptions(void) +{ + struct sigaction act; + volatile int val; + + act.sa_sigaction = sig_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO | SA_NODEFER; + sigaction(SIGFPE, &act, NULL); + sigaction(SIGILL, &act, NULL); + sigaction(SIGSEGV, &act, NULL); + sigaction(SIGBUS, &act, NULL); + sigaction(SIGTRAP, &act, NULL); + + /* test division by zero reporting */ + printf("DIVZ exception:\n"); + if (setjmp(jmp_env) == 0) { + /* now divide by zero */ + v1 = 0; + v1 = 2 / v1; + } + +#if !defined(__x86_64__) + printf("BOUND exception:\n"); + if (setjmp(jmp_env) == 0) { + /* bound exception */ + tab[0] = 1; + tab[1] = 10; + asm volatile ("bound %0, %1" : : "r" (11), "m" (tab[0])); + } +#endif + +#ifdef TEST_SEGS + printf("segment exceptions:\n"); + if (setjmp(jmp_env) == 0) { + /* load an invalid segment */ + asm volatile ("movl %0, %%fs" : : "r" ((0x1234 << 3) | 1)); + } + if (setjmp(jmp_env) == 0) { + /* null data segment is valid */ + asm volatile ("movl %0, %%fs" : : "r" (3)); + /* null stack segment */ + asm volatile ("movl %0, %%ss" : : "r" (3)); + } + + { + struct modify_ldt_ldt_s ldt; + ldt.entry_number = 1; + ldt.base_addr = (unsigned long)&seg_data1; + ldt.limit = (sizeof(seg_data1) + 0xfff) >> 12; + ldt.seg_32bit = 1; + ldt.contents = MODIFY_LDT_CONTENTS_DATA; + ldt.read_exec_only = 0; + ldt.limit_in_pages = 1; + ldt.seg_not_present = 1; + ldt.useable = 1; + modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */ + + if (setjmp(jmp_env) == 0) { + /* segment not present */ + asm volatile ("movl %0, %%fs" : : "r" (MK_SEL(1))); + } + } +#endif + + /* test SEGV reporting */ + printf("PF exception:\n"); + if (setjmp(jmp_env) == 0) { + val = 1; + /* we add a nop to test a weird PC retrieval case */ + asm volatile ("nop"); + /* now store in an invalid address */ + *(char *)0x1234 = 1; + } + + /* test SEGV reporting */ + printf("PF exception:\n"); + if (setjmp(jmp_env) == 0) { + val = 1; + /* read from an invalid address */ + v1 = *(char *)0x1234; + } + + /* test illegal instruction reporting */ + printf("UD2 exception:\n"); + if (setjmp(jmp_env) == 0) { + /* now execute an invalid instruction */ + asm volatile("ud2"); + } + printf("lock nop exception:\n"); + if (setjmp(jmp_env) == 0) { + /* now execute an invalid instruction */ + asm volatile("lock nop"); + } + + printf("INT exception:\n"); + if (setjmp(jmp_env) == 0) { + asm volatile ("int $0xfd"); + } + if (setjmp(jmp_env) == 0) { + asm volatile ("int $0x01"); + } + if (setjmp(jmp_env) == 0) { + asm volatile (".byte 0xcd, 0x03"); + } + if (setjmp(jmp_env) == 0) { + asm volatile ("int $0x04"); + } + if (setjmp(jmp_env) == 0) { + asm volatile ("int $0x05"); + } + + printf("INT3 exception:\n"); + if (setjmp(jmp_env) == 0) { + asm volatile ("int3"); + } + + printf("CLI exception:\n"); + if (setjmp(jmp_env) == 0) { + asm volatile ("cli"); + } + + printf("STI exception:\n"); + if (setjmp(jmp_env) == 0) { + asm volatile ("cli"); + } + +#if !defined(__x86_64__) + printf("INTO exception:\n"); + if (setjmp(jmp_env) == 0) { + /* overflow exception */ + asm volatile ("addl $1, %0 ; into" : : "r" (0x7fffffff)); + } +#endif + + printf("OUTB exception:\n"); + if (setjmp(jmp_env) == 0) { + asm volatile ("outb %%al, %%dx" : : "d" (0x4321), "a" (0)); + } + + printf("INB exception:\n"); + if (setjmp(jmp_env) == 0) { + asm volatile ("inb %%dx, %%al" : "=a" (val) : "d" (0x4321)); + } + + printf("REP OUTSB exception:\n"); + if (setjmp(jmp_env) == 0) { + asm volatile ("rep outsb" : : "d" (0x4321), "S" (tab), "c" (1)); + } + + printf("REP INSB exception:\n"); + if (setjmp(jmp_env) == 0) { + asm volatile ("rep insb" : : "d" (0x4321), "D" (tab), "c" (1)); + } + + printf("HLT exception:\n"); + if (setjmp(jmp_env) == 0) { + asm volatile ("hlt"); + } + + printf("single step exception:\n"); + val = 0; + if (setjmp(jmp_env) == 0) { + asm volatile ("pushf\n" + "orl $0x00100, (%%esp)\n" + "popf\n" + "movl $0xabcd, %0\n" + "movl $0x0, %0\n" : "=m" (val) : : "cc", "memory"); + } + printf("val=0x%x\n", val); +} + +#if !defined(__x86_64__) +/* specific precise single step test */ +void sig_trap_handler(int sig, siginfo_t *info, void *puc) +{ + struct ucontext *uc = puc; + printf("EIP=" FMTLX "\n", (long)uc->uc_mcontext.gregs[REG_EIP]); +} + +const uint8_t sstep_buf1[4] = { 1, 2, 3, 4}; +uint8_t sstep_buf2[4]; + +void test_single_step(void) +{ + struct sigaction act; + volatile int val; + int i; + + val = 0; + act.sa_sigaction = sig_trap_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + sigaction(SIGTRAP, &act, NULL); + asm volatile ("pushf\n" + "orl $0x00100, (%%esp)\n" + "popf\n" + "movl $0xabcd, %0\n" + + /* jmp test */ + "movl $3, %%ecx\n" + "1:\n" + "addl $1, %0\n" + "decl %%ecx\n" + "jnz 1b\n" + + /* movsb: the single step should stop at each movsb iteration */ + "movl $sstep_buf1, %%esi\n" + "movl $sstep_buf2, %%edi\n" + "movl $0, %%ecx\n" + "rep movsb\n" + "movl $3, %%ecx\n" + "rep movsb\n" + "movl $1, %%ecx\n" + "rep movsb\n" + + /* cmpsb: the single step should stop at each cmpsb iteration */ + "movl $sstep_buf1, %%esi\n" + "movl $sstep_buf2, %%edi\n" + "movl $0, %%ecx\n" + "rep cmpsb\n" + "movl $4, %%ecx\n" + "rep cmpsb\n" + + /* getpid() syscall: single step should skip one + instruction */ + "movl $20, %%eax\n" + "int $0x80\n" + "movl $0, %%eax\n" + + /* when modifying SS, trace is not done on the next + instruction */ + "movl %%ss, %%ecx\n" + "movl %%ecx, %%ss\n" + "addl $1, %0\n" + "movl $1, %%eax\n" + "movl %%ecx, %%ss\n" + "jmp 1f\n" + "addl $1, %0\n" + "1:\n" + "movl $1, %%eax\n" + "pushl %%ecx\n" + "popl %%ss\n" + "addl $1, %0\n" + "movl $1, %%eax\n" + + "pushf\n" + "andl $~0x00100, (%%esp)\n" + "popf\n" + : "=m" (val) + : + : "cc", "memory", "eax", "ecx", "esi", "edi"); + printf("val=%d\n", val); + for(i = 0; i < 4; i++) + printf("sstep_buf2[%d] = %d\n", i, sstep_buf2[i]); +} + +/* self modifying code test */ +uint8_t code[] = { + 0xb8, 0x1, 0x00, 0x00, 0x00, /* movl $1, %eax */ + 0xc3, /* ret */ +}; + +asm("smc_code2:\n" + "movl 4(%esp), %eax\n" + "movl %eax, smc_patch_addr2 + 1\n" + "nop\n" + "nop\n" + "nop\n" + "nop\n" + "nop\n" + "nop\n" + "nop\n" + "nop\n" + "smc_patch_addr2:\n" + "movl $1, %eax\n" + "ret\n"); + +typedef int FuncType(void); +extern int smc_code2(int); +void test_self_modifying_code(void) +{ + int i; + + printf("self modifying code:\n"); + printf("func1 = 0x%x\n", ((FuncType *)code)()); + for(i = 2; i <= 4; i++) { + code[1] = i; + printf("func%d = 0x%x\n", i, ((FuncType *)code)()); + } + + /* more difficult test : the modified code is just after the + modifying instruction. It is forbidden in Intel specs, but it + is used by old DOS programs */ + for(i = 2; i <= 4; i++) { + printf("smc_code2(%d) = %d\n", i, smc_code2(i)); + } +} +#endif + +long enter_stack[4096]; + +#if defined(__x86_64__) +#define RSP "%%rsp" +#define RBP "%%rbp" +#else +#define RSP "%%esp" +#define RBP "%%ebp" +#endif + +#define TEST_ENTER(size, stack_type, level)\ +{\ + long esp_save, esp_val, ebp_val, ebp_save, i;\ + stack_type *ptr, *stack_end, *stack_ptr;\ + memset(enter_stack, 0, sizeof(enter_stack));\ + stack_end = stack_ptr = (stack_type *)(enter_stack + 4096);\ + ebp_val = (long)stack_ptr;\ + for(i=1;i<=32;i++)\ + *--stack_ptr = i;\ + esp_val = (long)stack_ptr;\ + asm("mov " RSP ", %[esp_save]\n"\ + "mov " RBP ", %[ebp_save]\n"\ + "mov %[esp_val], " RSP "\n"\ + "mov %[ebp_val], " RBP "\n"\ + "enter" size " $8, $" #level "\n"\ + "mov " RSP ", %[esp_val]\n"\ + "mov " RBP ", %[ebp_val]\n"\ + "mov %[esp_save], " RSP "\n"\ + "mov %[ebp_save], " RBP "\n"\ + : [esp_save] "=r" (esp_save),\ + [ebp_save] "=r" (ebp_save),\ + [esp_val] "=r" (esp_val),\ + [ebp_val] "=r" (ebp_val)\ + : "[esp_val]" (esp_val),\ + "[ebp_val]" (ebp_val));\ + printf("level=%d:\n", level);\ + printf("esp_val=" FMTLX "\n", esp_val - (long)stack_end);\ + printf("ebp_val=" FMTLX "\n", ebp_val - (long)stack_end);\ + for(ptr = (stack_type *)esp_val; ptr < stack_end; ptr++)\ + printf(FMTLX "\n", (long)ptr[0]);\ +} + +static void test_enter(void) +{ +#if defined(__x86_64__) + TEST_ENTER("q", uint64_t, 0); + TEST_ENTER("q", uint64_t, 1); + TEST_ENTER("q", uint64_t, 2); + TEST_ENTER("q", uint64_t, 31); +#else + TEST_ENTER("l", uint32_t, 0); + TEST_ENTER("l", uint32_t, 1); + TEST_ENTER("l", uint32_t, 2); + TEST_ENTER("l", uint32_t, 31); +#endif + + TEST_ENTER("w", uint16_t, 0); + TEST_ENTER("w", uint16_t, 1); + TEST_ENTER("w", uint16_t, 2); + TEST_ENTER("w", uint16_t, 31); +} + +#ifdef TEST_SSE + +typedef int __m64 __attribute__ ((__mode__ (__V2SI__))); +typedef int __m128 __attribute__ ((__mode__(__V4SF__))); + +typedef union { + double d[2]; + float s[4]; + uint32_t l[4]; + uint64_t q[2]; + __m128 dq; +} XMMReg; + +static uint64_t __attribute__((aligned(16))) test_values[4][2] = { + { 0x456723c698694873, 0xdc515cff944a58ec }, + { 0x1f297ccd58bad7ab, 0x41f21efba9e3e146 }, + { 0x007c62c2085427f8, 0x231be9e8cde7438d }, + { 0x0f76255a085427f8, 0xc233e9e8c4c9439a }, +}; + +#define SSE_OP(op)\ +{\ + asm volatile (#op " %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\ + printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\ + #op,\ + a.q[1], a.q[0],\ + b.q[1], b.q[0],\ + r.q[1], r.q[0]);\ +} + +#define SSE_OP2(op)\ +{\ + int i;\ + for(i=0;i<2;i++) {\ + a.q[0] = test_values[2*i][0];\ + a.q[1] = test_values[2*i][1];\ + b.q[0] = test_values[2*i+1][0];\ + b.q[1] = test_values[2*i+1][1];\ + SSE_OP(op);\ + }\ +} + +#define MMX_OP2(op)\ +{\ + int i;\ + for(i=0;i<2;i++) {\ + a.q[0] = test_values[2*i][0];\ + b.q[0] = test_values[2*i+1][0];\ + asm volatile (#op " %2, %0" : "=y" (r.q[0]) : "0" (a.q[0]), "y" (b.q[0]));\ + printf("%-9s: a=" FMT64X " b=" FMT64X " r=" FMT64X "\n",\ + #op,\ + a.q[0],\ + b.q[0],\ + r.q[0]);\ + }\ + SSE_OP2(op);\ +} + +#define SHUF_OP(op, ib)\ +{\ + a.q[0] = test_values[0][0];\ + a.q[1] = test_values[0][1];\ + b.q[0] = test_values[1][0];\ + b.q[1] = test_values[1][1];\ + asm volatile (#op " $" #ib ", %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\ + printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\ + #op,\ + a.q[1], a.q[0],\ + b.q[1], b.q[0],\ + ib,\ + r.q[1], r.q[0]);\ +} + +#define PSHUF_OP(op, ib)\ +{\ + int i;\ + for(i=0;i<2;i++) {\ + a.q[0] = test_values[2*i][0];\ + a.q[1] = test_values[2*i][1];\ + asm volatile (#op " $" #ib ", %1, %0" : "=x" (r.dq) : "x" (a.dq));\ + printf("%-9s: a=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\ + #op,\ + a.q[1], a.q[0],\ + ib,\ + r.q[1], r.q[0]);\ + }\ +} + +#define SHIFT_IM(op, ib)\ +{\ + int i;\ + for(i=0;i<2;i++) {\ + a.q[0] = test_values[2*i][0];\ + a.q[1] = test_values[2*i][1];\ + asm volatile (#op " $" #ib ", %0" : "=x" (r.dq) : "0" (a.dq));\ + printf("%-9s: a=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\ + #op,\ + a.q[1], a.q[0],\ + ib,\ + r.q[1], r.q[0]);\ + }\ +} + +#define SHIFT_OP(op, ib)\ +{\ + int i;\ + SHIFT_IM(op, ib);\ + for(i=0;i<2;i++) {\ + a.q[0] = test_values[2*i][0];\ + a.q[1] = test_values[2*i][1];\ + b.q[0] = ib;\ + b.q[1] = 0;\ + asm volatile (#op " %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\ + printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\ + #op,\ + a.q[1], a.q[0],\ + b.q[1], b.q[0],\ + r.q[1], r.q[0]);\ + }\ +} + +#define MOVMSK(op)\ +{\ + int i, reg;\ + for(i=0;i<2;i++) {\ + a.q[0] = test_values[2*i][0];\ + a.q[1] = test_values[2*i][1];\ + asm volatile (#op " %1, %0" : "=r" (reg) : "x" (a.dq));\ + printf("%-9s: a=" FMT64X "" FMT64X " r=%08x\n",\ + #op,\ + a.q[1], a.q[0],\ + reg);\ + }\ +} + +#define SSE_OPS(a) \ +SSE_OP(a ## ps);\ +SSE_OP(a ## ss); + +#define SSE_OPD(a) \ +SSE_OP(a ## pd);\ +SSE_OP(a ## sd); + +#define SSE_COMI(op, field)\ +{\ + unsigned int eflags;\ + XMMReg a, b;\ + a.field[0] = a1;\ + b.field[0] = b1;\ + asm volatile (#op " %2, %1\n"\ + "pushf\n"\ + "pop %0\n"\ + : "=m" (eflags)\ + : "x" (a.dq), "x" (b.dq));\ + printf("%-9s: a=%f b=%f cc=%04x\n",\ + #op, a1, b1,\ + eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));\ +} + +void test_sse_comi(double a1, double b1) +{ + SSE_COMI(ucomiss, s); + SSE_COMI(ucomisd, d); + SSE_COMI(comiss, s); + SSE_COMI(comisd, d); +} + +#define CVT_OP_XMM(op)\ +{\ + asm volatile (#op " %1, %0" : "=x" (r.dq) : "x" (a.dq));\ + printf("%-9s: a=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\ + #op,\ + a.q[1], a.q[0],\ + r.q[1], r.q[0]);\ +} + +#define CVT_OP_XMM2MMX(op)\ +{\ + asm volatile (#op " %1, %0" : "=y" (r.q[0]) : "x" (a.dq));\ + printf("%-9s: a=" FMT64X "" FMT64X " r=" FMT64X "\n",\ + #op,\ + a.q[1], a.q[0],\ + r.q[0]);\ +} + +#define CVT_OP_MMX2XMM(op)\ +{\ + asm volatile (#op " %1, %0" : "=x" (r.dq) : "y" (a.q[0]));\ + printf("%-9s: a=" FMT64X " r=" FMT64X "" FMT64X "\n",\ + #op,\ + a.q[0],\ + r.q[1], r.q[0]);\ +} + +#define CVT_OP_REG2XMM(op)\ +{\ + asm volatile (#op " %1, %0" : "=x" (r.dq) : "r" (a.l[0]));\ + printf("%-9s: a=%08x r=" FMT64X "" FMT64X "\n",\ + #op,\ + a.l[0],\ + r.q[1], r.q[0]);\ +} + +#define CVT_OP_XMM2REG(op)\ +{\ + asm volatile (#op " %1, %0" : "=r" (r.l[0]) : "x" (a.dq));\ + printf("%-9s: a=" FMT64X "" FMT64X " r=%08x\n",\ + #op,\ + a.q[1], a.q[0],\ + r.l[0]);\ +} + +struct fpxstate { + uint16_t fpuc; + uint16_t fpus; + uint16_t fptag; + uint16_t fop; + uint32_t fpuip; + uint16_t cs_sel; + uint16_t dummy0; + uint32_t fpudp; + uint16_t ds_sel; + uint16_t dummy1; + uint32_t mxcsr; + uint32_t mxcsr_mask; + uint8_t fpregs1[8 * 16]; + uint8_t xmm_regs[8 * 16]; + uint8_t dummy2[224]; +}; + +static struct fpxstate fpx_state __attribute__((aligned(16))); +static struct fpxstate fpx_state2 __attribute__((aligned(16))); + +void test_fxsave(void) +{ + struct fpxstate *fp = &fpx_state; + struct fpxstate *fp2 = &fpx_state2; + int i, nb_xmm; + XMMReg a, b; + a.q[0] = test_values[0][0]; + a.q[1] = test_values[0][1]; + b.q[0] = test_values[1][0]; + b.q[1] = test_values[1][1]; + + asm("movdqa %2, %%xmm0\n" + "movdqa %3, %%xmm7\n" +#if defined(__x86_64__) + "movdqa %2, %%xmm15\n" +#endif + " fld1\n" + " fldpi\n" + " fldln2\n" + " fxsave %0\n" + " fxrstor %0\n" + " fxsave %1\n" + " fninit\n" + : "=m" (*(uint32_t *)fp2), "=m" (*(uint32_t *)fp) + : "m" (a), "m" (b)); + printf("fpuc=%04x\n", fp->fpuc); + printf("fpus=%04x\n", fp->fpus); + printf("fptag=%04x\n", fp->fptag); + for(i = 0; i < 3; i++) { + printf("ST%d: " FMT64X " %04x\n", + i, + *(uint64_t *)&fp->fpregs1[i * 16], + *(uint16_t *)&fp->fpregs1[i * 16 + 8]); + } + printf("mxcsr=%08x\n", fp->mxcsr & 0x1f80); +#if defined(__x86_64__) + nb_xmm = 16; +#else + nb_xmm = 8; +#endif + for(i = 0; i < nb_xmm; i++) { + printf("xmm%d: " FMT64X "" FMT64X "\n", + i, + *(uint64_t *)&fp->xmm_regs[i * 16], + *(uint64_t *)&fp->xmm_regs[i * 16 + 8]); + } +} + +void test_sse(void) +{ + XMMReg r, a, b; + int i; + + MMX_OP2(punpcklbw); + MMX_OP2(punpcklwd); + MMX_OP2(punpckldq); + MMX_OP2(packsswb); + MMX_OP2(pcmpgtb); + MMX_OP2(pcmpgtw); + MMX_OP2(pcmpgtd); + MMX_OP2(packuswb); + MMX_OP2(punpckhbw); + MMX_OP2(punpckhwd); + MMX_OP2(punpckhdq); + MMX_OP2(packssdw); + MMX_OP2(pcmpeqb); + MMX_OP2(pcmpeqw); + MMX_OP2(pcmpeqd); + + MMX_OP2(paddq); + MMX_OP2(pmullw); + MMX_OP2(psubusb); + MMX_OP2(psubusw); + MMX_OP2(pminub); + MMX_OP2(pand); + MMX_OP2(paddusb); + MMX_OP2(paddusw); + MMX_OP2(pmaxub); + MMX_OP2(pandn); + + MMX_OP2(pmulhuw); + MMX_OP2(pmulhw); + + MMX_OP2(psubsb); + MMX_OP2(psubsw); + MMX_OP2(pminsw); + MMX_OP2(por); + MMX_OP2(paddsb); + MMX_OP2(paddsw); + MMX_OP2(pmaxsw); + MMX_OP2(pxor); + MMX_OP2(pmuludq); + MMX_OP2(pmaddwd); + MMX_OP2(psadbw); + MMX_OP2(psubb); + MMX_OP2(psubw); + MMX_OP2(psubd); + MMX_OP2(psubq); + MMX_OP2(paddb); + MMX_OP2(paddw); + MMX_OP2(paddd); + + MMX_OP2(pavgb); + MMX_OP2(pavgw); + + asm volatile ("pinsrw $1, %1, %0" : "=y" (r.q[0]) : "r" (0x12345678)); + printf("%-9s: r=" FMT64X "\n", "pinsrw", r.q[0]); + + asm volatile ("pinsrw $5, %1, %0" : "=x" (r.dq) : "r" (0x12345678)); + printf("%-9s: r=" FMT64X "" FMT64X "\n", "pinsrw", r.q[1], r.q[0]); + + a.q[0] = test_values[0][0]; + a.q[1] = test_values[0][1]; + asm volatile ("pextrw $1, %1, %0" : "=r" (r.l[0]) : "y" (a.q[0])); + printf("%-9s: r=%08x\n", "pextrw", r.l[0]); + + asm volatile ("pextrw $5, %1, %0" : "=r" (r.l[0]) : "x" (a.dq)); + printf("%-9s: r=%08x\n", "pextrw", r.l[0]); + + asm volatile ("pmovmskb %1, %0" : "=r" (r.l[0]) : "y" (a.q[0])); + printf("%-9s: r=%08x\n", "pmovmskb", r.l[0]); + + asm volatile ("pmovmskb %1, %0" : "=r" (r.l[0]) : "x" (a.dq)); + printf("%-9s: r=%08x\n", "pmovmskb", r.l[0]); + + { + r.q[0] = -1; + r.q[1] = -1; + + a.q[0] = test_values[0][0]; + a.q[1] = test_values[0][1]; + b.q[0] = test_values[1][0]; + b.q[1] = test_values[1][1]; + asm volatile("maskmovq %1, %0" : + : "y" (a.q[0]), "y" (b.q[0]), "D" (&r) + : "memory"); + printf("%-9s: r=" FMT64X " a=" FMT64X " b=" FMT64X "\n", + "maskmov", + r.q[0], + a.q[0], + b.q[0]); + asm volatile("maskmovdqu %1, %0" : + : "x" (a.dq), "x" (b.dq), "D" (&r) + : "memory"); + printf("%-9s: r=" FMT64X "" FMT64X " a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X "\n", + "maskmov", + r.q[1], r.q[0], + a.q[1], a.q[0], + b.q[1], b.q[0]); + } + + asm volatile ("emms"); + + SSE_OP2(punpcklqdq); + SSE_OP2(punpckhqdq); + SSE_OP2(andps); + SSE_OP2(andpd); + SSE_OP2(andnps); + SSE_OP2(andnpd); + SSE_OP2(orps); + SSE_OP2(orpd); + SSE_OP2(xorps); + SSE_OP2(xorpd); + + SSE_OP2(unpcklps); + SSE_OP2(unpcklpd); + SSE_OP2(unpckhps); + SSE_OP2(unpckhpd); + + SHUF_OP(shufps, 0x78); + SHUF_OP(shufpd, 0x02); + + PSHUF_OP(pshufd, 0x78); + PSHUF_OP(pshuflw, 0x78); + PSHUF_OP(pshufhw, 0x78); + + SHIFT_OP(psrlw, 7); + SHIFT_OP(psrlw, 16); + SHIFT_OP(psraw, 7); + SHIFT_OP(psraw, 16); + SHIFT_OP(psllw, 7); + SHIFT_OP(psllw, 16); + + SHIFT_OP(psrld, 7); + SHIFT_OP(psrld, 32); + SHIFT_OP(psrad, 7); + SHIFT_OP(psrad, 32); + SHIFT_OP(pslld, 7); + SHIFT_OP(pslld, 32); + + SHIFT_OP(psrlq, 7); + SHIFT_OP(psrlq, 32); + SHIFT_OP(psllq, 7); + SHIFT_OP(psllq, 32); + + SHIFT_IM(psrldq, 16); + SHIFT_IM(psrldq, 7); + SHIFT_IM(pslldq, 16); + SHIFT_IM(pslldq, 7); + + MOVMSK(movmskps); + MOVMSK(movmskpd); + + /* FPU specific ops */ + + { + uint32_t mxcsr; + asm volatile("stmxcsr %0" : "=m" (mxcsr)); + printf("mxcsr=%08x\n", mxcsr & 0x1f80); + asm volatile("ldmxcsr %0" : : "m" (mxcsr)); + } + + test_sse_comi(2, -1); + test_sse_comi(2, 2); + test_sse_comi(2, 3); + test_sse_comi(2, q_nan.d); + test_sse_comi(q_nan.d, -1); + + for(i = 0; i < 2; i++) { + a.s[0] = 2.7; + a.s[1] = 3.4; + a.s[2] = 4; + a.s[3] = -6.3; + b.s[0] = 45.7; + b.s[1] = 353.4; + b.s[2] = 4; + b.s[3] = 56.3; + if (i == 1) { + a.s[0] = q_nan.d; + b.s[3] = q_nan.d; + } + + SSE_OPS(add); + SSE_OPS(mul); + SSE_OPS(sub); + SSE_OPS(min); + SSE_OPS(div); + SSE_OPS(max); + SSE_OPS(sqrt); + SSE_OPS(cmpeq); + SSE_OPS(cmplt); + SSE_OPS(cmple); + SSE_OPS(cmpunord); + SSE_OPS(cmpneq); + SSE_OPS(cmpnlt); + SSE_OPS(cmpnle); + SSE_OPS(cmpord); + + + a.d[0] = 2.7; + a.d[1] = -3.4; + b.d[0] = 45.7; + b.d[1] = -53.4; + if (i == 1) { + a.d[0] = q_nan.d; + b.d[1] = q_nan.d; + } + SSE_OPD(add); + SSE_OPD(mul); + SSE_OPD(sub); + SSE_OPD(min); + SSE_OPD(div); + SSE_OPD(max); + SSE_OPD(sqrt); + SSE_OPD(cmpeq); + SSE_OPD(cmplt); + SSE_OPD(cmple); + SSE_OPD(cmpunord); + SSE_OPD(cmpneq); + SSE_OPD(cmpnlt); + SSE_OPD(cmpnle); + SSE_OPD(cmpord); + } + + /* float to float/int */ + a.s[0] = 2.7; + a.s[1] = 3.4; + a.s[2] = 4; + a.s[3] = -6.3; + CVT_OP_XMM(cvtps2pd); + CVT_OP_XMM(cvtss2sd); + CVT_OP_XMM2MMX(cvtps2pi); + CVT_OP_XMM2MMX(cvttps2pi); + CVT_OP_XMM2REG(cvtss2si); + CVT_OP_XMM2REG(cvttss2si); + CVT_OP_XMM(cvtps2dq); + CVT_OP_XMM(cvttps2dq); + + a.d[0] = 2.6; + a.d[1] = -3.4; + CVT_OP_XMM(cvtpd2ps); + CVT_OP_XMM(cvtsd2ss); + CVT_OP_XMM2MMX(cvtpd2pi); + CVT_OP_XMM2MMX(cvttpd2pi); + CVT_OP_XMM2REG(cvtsd2si); + CVT_OP_XMM2REG(cvttsd2si); + CVT_OP_XMM(cvtpd2dq); + CVT_OP_XMM(cvttpd2dq); + + /* int to float */ + a.l[0] = -6; + a.l[1] = 2; + a.l[2] = 100; + a.l[3] = -60000; + CVT_OP_MMX2XMM(cvtpi2ps); + CVT_OP_MMX2XMM(cvtpi2pd); + CVT_OP_REG2XMM(cvtsi2ss); + CVT_OP_REG2XMM(cvtsi2sd); + CVT_OP_XMM(cvtdq2ps); + CVT_OP_XMM(cvtdq2pd); + + /* XXX: test PNI insns */ +#if 0 + SSE_OP2(movshdup); +#endif + asm volatile ("emms"); +} + +#endif + +extern void *__start_initcall; +extern void *__stop_initcall; + + +int main(int argc, char **argv) +{ + void **ptr; + void (*func)(void); + + ptr = &__start_initcall; + while (ptr != &__stop_initcall) { + func = *ptr++; + func(); + } + test_bsx(); + test_mul(); + test_jcc(); + test_floats(); +#if !defined(__x86_64__) + test_bcd(); +#endif + test_xchg(); + test_string(); + test_misc(); + test_lea(); +#ifdef TEST_SEGS + test_segs(); + test_code16(); +#endif +#ifdef TEST_VM86 + test_vm86(); +#endif + test_exceptions(); +#if !defined(__x86_64__) + test_self_modifying_code(); + test_single_step(); +#endif + test_enter(); +#ifdef TEST_SSE + test_sse(); + test_fxsave(); +#endif + return 0; +} diff --git a/tools/ioemu/tests/test-i386.h b/tools/ioemu/tests/test-i386.h new file mode 100644 index 0000000000..75106b8ce2 --- /dev/null +++ b/tools/ioemu/tests/test-i386.h @@ -0,0 +1,152 @@ + +#define exec_op glue(exec_, OP) +#define exec_opq glue(glue(exec_, OP), q) +#define exec_opl glue(glue(exec_, OP), l) +#define exec_opw glue(glue(exec_, OP), w) +#define exec_opb glue(glue(exec_, OP), b) + +#define EXECOP2(size, rsize, res, s1, flags) \ + asm ("push %4\n\t"\ + "popf\n\t"\ + stringify(OP) size " %" rsize "2, %" rsize "0\n\t" \ + "pushf\n\t"\ + "pop %1\n\t"\ + : "=q" (res), "=g" (flags)\ + : "q" (s1), "0" (res), "1" (flags)); \ + printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n", \ + stringify(OP) size, s0, s1, res, iflags, flags & CC_MASK); + +#define EXECOP1(size, rsize, res, flags) \ + asm ("push %3\n\t"\ + "popf\n\t"\ + stringify(OP) size " %" rsize "0\n\t" \ + "pushf\n\t"\ + "pop %1\n\t"\ + : "=q" (res), "=g" (flags)\ + : "0" (res), "1" (flags)); \ + printf("%-10s A=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n", \ + stringify(OP) size, s0, res, iflags, flags & CC_MASK); + +#ifdef OP1 +#if defined(__x86_64__) +void exec_opq(long s0, long s1, long iflags) +{ + long res, flags; + res = s0; + flags = iflags; + EXECOP1("q", "", res, flags); +} +#endif + +void exec_opl(long s0, long s1, long iflags) +{ + long res, flags; + res = s0; + flags = iflags; + EXECOP1("l", "k", res, flags); +} + +void exec_opw(long s0, long s1, long iflags) +{ + long res, flags; + res = s0; + flags = iflags; + EXECOP1("w", "w", res, flags); +} + +void exec_opb(long s0, long s1, long iflags) +{ + long res, flags; + res = s0; + flags = iflags; + EXECOP1("b", "b", res, flags); +} +#else +#if defined(__x86_64__) +void exec_opq(long s0, long s1, long iflags) +{ + long res, flags; + res = s0; + flags = iflags; + EXECOP2("q", "", res, s1, flags); +} +#endif + +void exec_opl(long s0, long s1, long iflags) +{ + long res, flags; + res = s0; + flags = iflags; + EXECOP2("l", "k", res, s1, flags); +} + +void exec_opw(long s0, long s1, long iflags) +{ + long res, flags; + res = s0; + flags = iflags; + EXECOP2("w", "w", res, s1, flags); +} + +void exec_opb(long s0, long s1, long iflags) +{ + long res, flags; + res = s0; + flags = iflags; + EXECOP2("b", "b", res, s1, flags); +} +#endif + +void exec_op(long s0, long s1) +{ + s0 = i2l(s0); + s1 = i2l(s1); +#if defined(__x86_64__) + exec_opq(s0, s1, 0); +#endif + exec_opl(s0, s1, 0); + exec_opw(s0, s1, 0); + exec_opb(s0, s1, 0); +#ifdef OP_CC +#if defined(__x86_64__) + exec_opq(s0, s1, CC_C); +#endif + exec_opl(s0, s1, CC_C); + exec_opw(s0, s1, CC_C); + exec_opb(s0, s1, CC_C); +#endif +} + +void glue(test_, OP)(void) +{ + exec_op(0x12345678, 0x812FADA); + exec_op(0x12341, 0x12341); + exec_op(0x12341, -0x12341); + exec_op(0xffffffff, 0); + exec_op(0xffffffff, -1); + exec_op(0xffffffff, 1); + exec_op(0xffffffff, 2); + exec_op(0x7fffffff, 0); + exec_op(0x7fffffff, 1); + exec_op(0x7fffffff, -1); + exec_op(0x80000000, -1); + exec_op(0x80000000, 1); + exec_op(0x80000000, -2); + exec_op(0x12347fff, 0); + exec_op(0x12347fff, 1); + exec_op(0x12347fff, -1); + exec_op(0x12348000, -1); + exec_op(0x12348000, 1); + exec_op(0x12348000, -2); + exec_op(0x12347f7f, 0); + exec_op(0x12347f7f, 1); + exec_op(0x12347f7f, -1); + exec_op(0x12348080, -1); + exec_op(0x12348080, 1); + exec_op(0x12348080, -2); +} + +void *glue(_test_, OP) __init_call = glue(test_, OP); + +#undef OP +#undef OP_CC diff --git a/tools/ioemu/tests/test_path.c b/tools/ioemu/tests/test_path.c new file mode 100644 index 0000000000..a9b52de378 --- /dev/null +++ b/tools/ioemu/tests/test_path.c @@ -0,0 +1,152 @@ +/* Test path override code */ +#define _GNU_SOURCE +#include "../path.c" +#include +#include +#include + +/* Any log message kills the test. */ +void gemu_log(const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "FATAL: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + exit(1); +} + +#define NO_CHANGE(_path) \ + do { \ + if (strcmp(path(_path), _path) != 0) return __LINE__; \ + } while(0) + +#define CHANGE_TO(_path, _newpath) \ + do { \ + if (strcmp(path(_path), _newpath) != 0) return __LINE__; \ + } while(0) + +static void cleanup(void) +{ + unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE"); + unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE2"); + unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE3"); + unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE4"); + unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE5"); + rmdir("/tmp/qemu-test_path/DIR1/DIR2"); + rmdir("/tmp/qemu-test_path/DIR1/DIR3"); + rmdir("/tmp/qemu-test_path/DIR1"); + rmdir("/tmp/qemu-test_path"); +} + +static unsigned int do_test(void) +{ + if (mkdir("/tmp/qemu-test_path", 0700) != 0) + return __LINE__; + + if (mkdir("/tmp/qemu-test_path/DIR1", 0700) != 0) + return __LINE__; + + if (mkdir("/tmp/qemu-test_path/DIR1/DIR2", 0700) != 0) + return __LINE__; + + if (mkdir("/tmp/qemu-test_path/DIR1/DIR3", 0700) != 0) + return __LINE__; + + if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE", 0600)) != 0) + return __LINE__; + + if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE2", 0600)) != 0) + return __LINE__; + + if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE3", 0600)) != 0) + return __LINE__; + + if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE4", 0600)) != 0) + return __LINE__; + + if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE5", 0600)) != 0) + return __LINE__; + + init_paths("/tmp/qemu-test_path"); + + NO_CHANGE("/tmp"); + NO_CHANGE("/tmp/"); + NO_CHANGE("/tmp/qemu-test_path"); + NO_CHANGE("/tmp/qemu-test_path/"); + NO_CHANGE("/tmp/qemu-test_path/D"); + NO_CHANGE("/tmp/qemu-test_path/DI"); + NO_CHANGE("/tmp/qemu-test_path/DIR"); + NO_CHANGE("/tmp/qemu-test_path/DIR1"); + NO_CHANGE("/tmp/qemu-test_path/DIR1/"); + + NO_CHANGE("/D"); + NO_CHANGE("/DI"); + NO_CHANGE("/DIR"); + NO_CHANGE("/DIR2"); + NO_CHANGE("/DIR1."); + + CHANGE_TO("/DIR1", "/tmp/qemu-test_path/DIR1"); + CHANGE_TO("/DIR1/", "/tmp/qemu-test_path/DIR1"); + + NO_CHANGE("/DIR1/D"); + NO_CHANGE("/DIR1/DI"); + NO_CHANGE("/DIR1/DIR"); + NO_CHANGE("/DIR1/DIR1"); + + CHANGE_TO("/DIR1/DIR2", "/tmp/qemu-test_path/DIR1/DIR2"); + CHANGE_TO("/DIR1/DIR2/", "/tmp/qemu-test_path/DIR1/DIR2"); + + CHANGE_TO("/DIR1/DIR3", "/tmp/qemu-test_path/DIR1/DIR3"); + CHANGE_TO("/DIR1/DIR3/", "/tmp/qemu-test_path/DIR1/DIR3"); + + NO_CHANGE("/DIR1/DIR2/F"); + NO_CHANGE("/DIR1/DIR2/FI"); + NO_CHANGE("/DIR1/DIR2/FIL"); + NO_CHANGE("/DIR1/DIR2/FIL."); + + CHANGE_TO("/DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); + CHANGE_TO("/DIR1/DIR2/FILE2", "/tmp/qemu-test_path/DIR1/DIR2/FILE2"); + CHANGE_TO("/DIR1/DIR2/FILE3", "/tmp/qemu-test_path/DIR1/DIR2/FILE3"); + CHANGE_TO("/DIR1/DIR2/FILE4", "/tmp/qemu-test_path/DIR1/DIR2/FILE4"); + CHANGE_TO("/DIR1/DIR2/FILE5", "/tmp/qemu-test_path/DIR1/DIR2/FILE5"); + + NO_CHANGE("/DIR1/DIR2/FILE6"); + NO_CHANGE("/DIR1/DIR2/FILE/X"); + + CHANGE_TO("/DIR1/../DIR1", "/tmp/qemu-test_path/DIR1"); + CHANGE_TO("/DIR1/../DIR1/", "/tmp/qemu-test_path/DIR1"); + CHANGE_TO("/../DIR1", "/tmp/qemu-test_path/DIR1"); + CHANGE_TO("/../DIR1/", "/tmp/qemu-test_path/DIR1"); + CHANGE_TO("/DIR1/DIR2/../DIR2", "/tmp/qemu-test_path/DIR1/DIR2"); + CHANGE_TO("/DIR1/DIR2/../DIR2/../../DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); + CHANGE_TO("/DIR1/DIR2/../DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); + + NO_CHANGE("/DIR1/DIR2/../DIR1"); + NO_CHANGE("/DIR1/DIR2/../FILE"); + + CHANGE_TO("/./DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); + CHANGE_TO("/././DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); + CHANGE_TO("/DIR1/./DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); + CHANGE_TO("/DIR1/././DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); + CHANGE_TO("/DIR1/DIR2/./FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); + CHANGE_TO("/DIR1/DIR2/././FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); + CHANGE_TO("/./DIR1/./DIR2/./FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); + + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret; + + ret = do_test(); + cleanup(); + if (ret) { + fprintf(stderr, "test_path: failed on line %i\n", ret); + return 1; + } + return 0; +} + diff --git a/tools/ioemu/tests/testthread.c b/tools/ioemu/tests/testthread.c new file mode 100644 index 0000000000..27e4825bc6 --- /dev/null +++ b/tools/ioemu/tests/testthread.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void *thread1_func(void *arg) +{ + int i; + char buf[512]; + + for(i=0;i<10;i++) { + snprintf(buf, sizeof(buf), "thread1: %d %s\n", i, (char *)arg); + write(1, buf, strlen(buf)); + usleep(100 * 1000); + } + return NULL; +} + +void *thread2_func(void *arg) +{ + int i; + char buf[512]; + for(i=0;i<20;i++) { + snprintf(buf, sizeof(buf), "thread2: %d %s\n", i, (char *)arg); + write(1, buf, strlen(buf)); + usleep(150 * 1000); + } + return NULL; +} + +void test_pthread(void) +{ + pthread_t tid1, tid2; + + pthread_create(&tid1, NULL, thread1_func, "hello1"); + pthread_create(&tid2, NULL, thread2_func, "hello2"); + pthread_join(tid1, NULL); + pthread_join(tid2, NULL); + printf("End of pthread test.\n"); +} + +int main(int argc, char **argv) +{ + test_pthread(); + return 0; +} diff --git a/tools/ioemu/texi2pod.pl b/tools/ioemu/texi2pod.pl new file mode 100644 index 0000000000..176627e9b9 --- /dev/null +++ b/tools/ioemu/texi2pod.pl @@ -0,0 +1,428 @@ +#! /usr/bin/perl -w + +# Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + +# This file is part of GNU CC. + +# GNU CC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# GNU CC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with GNU CC; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston MA 02111-1307, USA. + +# This does trivial (and I mean _trivial_) conversion of Texinfo +# markup to Perl POD format. It's intended to be used to extract +# something suitable for a manpage from a Texinfo document. + +$output = 0; +$skipping = 0; +%sects = (); +$section = ""; +@icstack = (); +@endwstack = (); +@skstack = (); +@instack = (); +$shift = ""; +%defs = (); +$fnno = 1; +$inf = ""; +$ibase = ""; + +while ($_ = shift) { + if (/^-D(.*)$/) { + if ($1 ne "") { + $flag = $1; + } else { + $flag = shift; + } + $value = ""; + ($flag, $value) = ($flag =~ /^([^=]+)(?:=(.+))?/); + die "no flag specified for -D\n" + unless $flag ne ""; + die "flags may only contain letters, digits, hyphens, dashes and underscores\n" + unless $flag =~ /^[a-zA-Z0-9_-]+$/; + $defs{$flag} = $value; + } elsif (/^-/) { + usage(); + } else { + $in = $_, next unless defined $in; + $out = $_, next unless defined $out; + usage(); + } +} + +if (defined $in) { + $inf = gensym(); + open($inf, "<$in") or die "opening \"$in\": $!\n"; + $ibase = $1 if $in =~ m|^(.+)/[^/]+$|; +} else { + $inf = \*STDIN; +} + +if (defined $out) { + open(STDOUT, ">$out") or die "opening \"$out\": $!\n"; +} + +while(defined $inf) { +while(<$inf>) { + # Certain commands are discarded without further processing. + /^\@(?: + [a-z]+index # @*index: useful only in complete manual + |need # @need: useful only in printed manual + |(?:end\s+)?group # @group .. @end group: ditto + |page # @page: ditto + |node # @node: useful only in .info file + |(?:end\s+)?ifnottex # @ifnottex .. @end ifnottex: use contents + )\b/x and next; + + chomp; + + # Look for filename and title markers. + /^\@setfilename\s+([^.]+)/ and $fn = $1, next; + /^\@settitle\s+([^.]+)/ and $tl = postprocess($1), next; + + # Identify a man title but keep only the one we are interested in. + /^\@c\s+man\s+title\s+([A-Za-z0-9-]+)\s+(.+)/ and do { + if (exists $defs{$1}) { + $fn = $1; + $tl = postprocess($2); + } + next; + }; + + # Look for blocks surrounded by @c man begin SECTION ... @c man end. + # This really oughta be @ifman ... @end ifman and the like, but such + # would require rev'ing all other Texinfo translators. + /^\@c\s+man\s+begin\s+([A-Z]+)\s+([A-Za-z0-9-]+)/ and do { + $output = 1 if exists $defs{$2}; + $sect = $1; + next; + }; + /^\@c\s+man\s+begin\s+([A-Z]+)/ and $sect = $1, $output = 1, next; + /^\@c\s+man\s+end/ and do { + $sects{$sect} = "" unless exists $sects{$sect}; + $sects{$sect} .= postprocess($section); + $section = ""; + $output = 0; + next; + }; + + # handle variables + /^\@set\s+([a-zA-Z0-9_-]+)\s*(.*)$/ and do { + $defs{$1} = $2; + next; + }; + /^\@clear\s+([a-zA-Z0-9_-]+)/ and do { + delete $defs{$1}; + next; + }; + + next unless $output; + + # Discard comments. (Can't do it above, because then we'd never see + # @c man lines.) + /^\@c\b/ and next; + + # End-block handler goes up here because it needs to operate even + # if we are skipping. + /^\@end\s+([a-z]+)/ and do { + # Ignore @end foo, where foo is not an operation which may + # cause us to skip, if we are presently skipping. + my $ended = $1; + next if $skipping && $ended !~ /^(?:ifset|ifclear|ignore|menu|iftex)$/; + + die "\@end $ended without \@$ended at line $.\n" unless defined $endw; + die "\@$endw ended by \@end $ended at line $.\n" unless $ended eq $endw; + + $endw = pop @endwstack; + + if ($ended =~ /^(?:ifset|ifclear|ignore|menu|iftex)$/) { + $skipping = pop @skstack; + next; + } elsif ($ended =~ /^(?:example|smallexample|display)$/) { + $shift = ""; + $_ = ""; # need a paragraph break + } elsif ($ended =~ /^(?:itemize|enumerate|[fv]?table)$/) { + $_ = "\n=back\n"; + $ic = pop @icstack; + } else { + die "unknown command \@end $ended at line $.\n"; + } + }; + + # We must handle commands which can cause skipping even while we + # are skipping, otherwise we will not process nested conditionals + # correctly. + /^\@ifset\s+([a-zA-Z0-9_-]+)/ and do { + push @endwstack, $endw; + push @skstack, $skipping; + $endw = "ifset"; + $skipping = 1 unless exists $defs{$1}; + next; + }; + + /^\@ifclear\s+([a-zA-Z0-9_-]+)/ and do { + push @endwstack, $endw; + push @skstack, $skipping; + $endw = "ifclear"; + $skipping = 1 if exists $defs{$1}; + next; + }; + + /^\@(ignore|menu|iftex)\b/ and do { + push @endwstack, $endw; + push @skstack, $skipping; + $endw = $1; + $skipping = 1; + next; + }; + + next if $skipping; + + # Character entities. First the ones that can be replaced by raw text + # or discarded outright: + s/\@copyright\{\}/(c)/g; + s/\@dots\{\}/.../g; + s/\@enddots\{\}/..../g; + s/\@([.!? ])/$1/g; + s/\@[:-]//g; + s/\@bullet(?:\{\})?/*/g; + s/\@TeX\{\}/TeX/g; + s/\@pounds\{\}/\#/g; + s/\@minus(?:\{\})?/-/g; + s/\\,/,/g; + + # Now the ones that have to be replaced by special escapes + # (which will be turned back into text by unmunge()) + s/&/&/g; + s/\@\{/{/g; + s/\@\}/}/g; + s/\@\@/&at;/g; + + # Inside a verbatim block, handle @var specially. + if ($shift ne "") { + s/\@var\{([^\}]*)\}/<$1>/g; + } + + # POD doesn't interpret E<> inside a verbatim block. + if ($shift eq "") { + s//>/g; + } else { + s//>/g; + } + + # Single line command handlers. + + /^\@include\s+(.+)$/ and do { + push @instack, $inf; + $inf = gensym(); + + # Try cwd and $ibase. + open($inf, "<" . $1) + or open($inf, "<" . $ibase . "/" . $1) + or die "cannot open $1 or $ibase/$1: $!\n"; + next; + }; + + /^\@(?:section|unnumbered|unnumberedsec|center)\s+(.+)$/ + and $_ = "\n=head2 $1\n"; + /^\@subsection\s+(.+)$/ + and $_ = "\n=head3 $1\n"; + + # Block command handlers: + /^\@itemize\s+(\@[a-z]+|\*|-)/ and do { + push @endwstack, $endw; + push @icstack, $ic; + $ic = $1; + $_ = "\n=over 4\n"; + $endw = "itemize"; + }; + + /^\@enumerate(?:\s+([a-zA-Z0-9]+))?/ and do { + push @endwstack, $endw; + push @icstack, $ic; + if (defined $1) { + $ic = $1 . "."; + } else { + $ic = "1."; + } + $_ = "\n=over 4\n"; + $endw = "enumerate"; + }; + + /^\@([fv]?table)\s+(\@[a-z]+)/ and do { + push @endwstack, $endw; + push @icstack, $ic; + $endw = $1; + $ic = $2; + $ic =~ s/\@(?:samp|strong|key|gcctabopt|option|env)/B/; + $ic =~ s/\@(?:code|kbd)/C/; + $ic =~ s/\@(?:dfn|var|emph|cite|i)/I/; + $ic =~ s/\@(?:file)/F/; + $_ = "\n=over 4\n"; + }; + + /^\@((?:small)?example|display)/ and do { + push @endwstack, $endw; + $endw = $1; + $shift = "\t"; + $_ = ""; # need a paragraph break + }; + + /^\@itemx?\s*(.+)?$/ and do { + if (defined $1) { + # Entity escapes prevent munging by the <> processing below. +# print "$ic\n"; + $_ = "\n=item $ic\<$1\>\n"; + } else { + $_ = "\n=item $ic\n"; + $ic =~ y/A-Ya-y/B-Zb-z/; + $ic =~ s/(\d+)/$1 + 1/eg; + } + }; + + $section .= $shift.$_."\n"; +} +# End of current file. +close($inf); +$inf = pop @instack; +} + +die "No filename or title\n" unless defined $fn && defined $tl; + +$sects{NAME} = "$fn \- $tl\n"; +$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES}; + +for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES + BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) { + if(exists $sects{$sect}) { + $head = $sect; + $head =~ s/SEEALSO/SEE ALSO/; + print "=head1 $head\n\n"; + print scalar unmunge ($sects{$sect}); + print "\n"; + } +} + +sub usage +{ + die "usage: $0 [-D toggle...] [infile [outfile]]\n"; +} + +sub postprocess +{ + local $_ = $_[0]; + + # @value{foo} is replaced by whatever 'foo' is defined as. + while (m/(\@value\{([a-zA-Z0-9_-]+)\})/g) { + if (! exists $defs{$2}) { + print STDERR "Option $2 not defined\n"; + s/\Q$1\E//; + } else { + $value = $defs{$2}; + s/\Q$1\E/$value/; + } + } + + # Formatting commands. + # Temporary escape for @r. + s/\@r\{([^\}]*)\}/R<$1>/g; + s/\@(?:dfn|var|emph|cite|i)\{([^\}]*)\}/I<$1>/g; + s/\@(?:code|kbd)\{([^\}]*)\}/C<$1>/g; + s/\@(?:gccoptlist|samp|strong|key|option|env|command|b)\{([^\}]*)\}/B<$1>/g; + s/\@sc\{([^\}]*)\}/\U$1/g; + s/\@file\{([^\}]*)\}/F<$1>/g; + s/\@w\{([^\}]*)\}/S<$1>/g; + s/\@(?:dmn|math)\{([^\}]*)\}/$1/g; + + # Cross references are thrown away, as are @noindent and @refill. + # (@noindent is impossible in .pod, and @refill is unnecessary.) + # @* is also impossible in .pod; we discard it and any newline that + # follows it. Similarly, our macro @gol must be discarded. + + s/\(?\@xref\{(?:[^\}]*)\}(?:[^.<]|(?:<[^<>]*>))*\.\)?//g; + s/\s+\(\@pxref\{(?:[^\}]*)\}\)//g; + s/;\s+\@pxref\{(?:[^\}]*)\}//g; + s/\@noindent\s*//g; + s/\@refill//g; + s/\@gol//g; + s/\@\*\s*\n?//g; + + # @uref can take one, two, or three arguments, with different + # semantics each time. @url and @email are just like @uref with + # one argument, for our purposes. + s/\@(?:uref|url|email)\{([^\},]*)\}/<B<$1>>/g; + s/\@uref\{([^\},]*),([^\},]*)\}/$2 (C<$1>)/g; + s/\@uref\{([^\},]*),([^\},]*),([^\},]*)\}/$3/g; + + # Turn B blah> into B I B to + # match Texinfo semantics of @emph inside @samp. Also handle @r + # inside bold. + s/<//g; + 1 while s/B<((?:[^<>]|I<[^<>]*>)*)R<([^>]*)>/B<$1>${2}B]*)I<([^>]+)>/B<$1>I<$2>B]*)B<([^>]+)>/I<$1>B<$2>I//g; + s/([BI])<(\s+)([^>]+)>/$2$1<$3>/g; + s/([BI])<([^>]+?)(\s+)>/$1<$2>$3/g; + + # Extract footnotes. This has to be done after all other + # processing because otherwise the regexp will choke on formatting + # inside @footnote. + while (/\@footnote/g) { + s/\@footnote\{([^\}]+)\}/[$fnno]/; + add_footnote($1, $fnno); + $fnno++; + } + + return $_; +} + +sub unmunge +{ + # Replace escaped symbols with their equivalents. + local $_ = $_[0]; + + s/</E/g; + s/>/E/g; + s/{/\{/g; + s/}/\}/g; + s/&at;/\@/g; + s/&/&/g; + return $_; +} + +sub add_footnote +{ + unless (exists $sects{FOOTNOTES}) { + $sects{FOOTNOTES} = "\n=over 4\n\n"; + } + + $sects{FOOTNOTES} .= "=item $fnno.\n\n"; $fnno++; + $sects{FOOTNOTES} .= $_[0]; + $sects{FOOTNOTES} .= "\n\n"; +} + +# stolen from Symbol.pm +{ + my $genseq = 0; + sub gensym + { + my $name = "GEN" . $genseq++; + my $ref = \*{$name}; + delete $::{$name}; + return $ref; + } +} diff --git a/tools/ioemu/thunk.c b/tools/ioemu/thunk.c new file mode 100644 index 0000000000..bc9bd28819 --- /dev/null +++ b/tools/ioemu/thunk.c @@ -0,0 +1,243 @@ +/* + * Generic thunking code to convert data between host and target CPU + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include + +#include "qemu.h" +#include "thunk.h" + +//#define DEBUG + +#define MAX_STRUCTS 128 + +/* XXX: make it dynamic */ +StructEntry struct_entries[MAX_STRUCTS]; + +static inline const argtype *thunk_type_next(const argtype *type_ptr) +{ + int type; + + type = *type_ptr++; + switch(type) { + case TYPE_CHAR: + case TYPE_SHORT: + case TYPE_INT: + case TYPE_LONGLONG: + case TYPE_ULONGLONG: + case TYPE_LONG: + case TYPE_ULONG: + case TYPE_PTRVOID: + return type_ptr; + case TYPE_PTR: + return thunk_type_next(type_ptr); + case TYPE_ARRAY: + return thunk_type_next(type_ptr + 1); + case TYPE_STRUCT: + return type_ptr + 1; + default: + return NULL; + } +} + +void thunk_register_struct(int id, const char *name, const argtype *types) +{ + const argtype *type_ptr; + StructEntry *se; + int nb_fields, offset, max_align, align, size, i, j; + + se = struct_entries + id; + + /* first we count the number of fields */ + type_ptr = types; + nb_fields = 0; + while (*type_ptr != TYPE_NULL) { + type_ptr = thunk_type_next(type_ptr); + nb_fields++; + } + se->field_types = types; + se->nb_fields = nb_fields; + se->name = name; +#ifdef DEBUG + printf("struct %s: id=%d nb_fields=%d\n", + se->name, id, se->nb_fields); +#endif + /* now we can alloc the data */ + + for(i = 0;i < 2; i++) { + offset = 0; + max_align = 1; + se->field_offsets[i] = malloc(nb_fields * sizeof(int)); + type_ptr = se->field_types; + for(j = 0;j < nb_fields; j++) { + size = thunk_type_size(type_ptr, i); + align = thunk_type_align(type_ptr, i); + offset = (offset + align - 1) & ~(align - 1); + se->field_offsets[i][j] = offset; + offset += size; + if (align > max_align) + max_align = align; + type_ptr = thunk_type_next(type_ptr); + } + offset = (offset + max_align - 1) & ~(max_align - 1); + se->size[i] = offset; + se->align[i] = max_align; +#ifdef DEBUG + printf("%s: size=%d align=%d\n", + i == THUNK_HOST ? "host" : "target", offset, max_align); +#endif + } +} + +void thunk_register_struct_direct(int id, const char *name, StructEntry *se1) +{ + StructEntry *se; + se = struct_entries + id; + *se = *se1; + se->name = name; +} + + +/* now we can define the main conversion functions */ +const argtype *thunk_convert(void *dst, const void *src, + const argtype *type_ptr, int to_host) +{ + int type; + + type = *type_ptr++; + switch(type) { + case TYPE_CHAR: + *(uint8_t *)dst = *(uint8_t *)src; + break; + case TYPE_SHORT: + *(uint16_t *)dst = tswap16(*(uint16_t *)src); + break; + case TYPE_INT: + *(uint32_t *)dst = tswap32(*(uint32_t *)src); + break; + case TYPE_LONGLONG: + case TYPE_ULONGLONG: + *(uint64_t *)dst = tswap64(*(uint64_t *)src); + break; +#if HOST_LONG_BITS == 32 && TARGET_LONG_BITS == 32 + case TYPE_LONG: + case TYPE_ULONG: + case TYPE_PTRVOID: + *(uint32_t *)dst = tswap32(*(uint32_t *)src); + break; +#elif HOST_LONG_BITS == 64 && TARGET_LONG_BITS == 32 + case TYPE_LONG: + case TYPE_ULONG: + case TYPE_PTRVOID: + if (to_host) { + *(uint64_t *)dst = tswap32(*(uint32_t *)src); + } else { + *(uint32_t *)dst = tswap32(*(uint64_t *)src & 0xffffffff); + } + break; +#else +#warning unsupported conversion +#endif + case TYPE_ARRAY: + { + int array_length, i, dst_size, src_size; + const uint8_t *s; + uint8_t *d; + + array_length = *type_ptr++; + dst_size = thunk_type_size(type_ptr, to_host); + src_size = thunk_type_size(type_ptr, 1 - to_host); + d = dst; + s = src; + for(i = 0;i < array_length; i++) { + thunk_convert(d, s, type_ptr, to_host); + d += dst_size; + s += src_size; + } + type_ptr = thunk_type_next(type_ptr); + } + break; + case TYPE_STRUCT: + { + int i; + const StructEntry *se; + const uint8_t *s; + uint8_t *d; + const argtype *field_types; + const int *dst_offsets, *src_offsets; + + se = struct_entries + *type_ptr++; + if (se->convert[0] != NULL) { + /* specific conversion is needed */ + (*se->convert[to_host])(dst, src); + } else { + /* standard struct conversion */ + field_types = se->field_types; + dst_offsets = se->field_offsets[to_host]; + src_offsets = se->field_offsets[1 - to_host]; + d = dst; + s = src; + for(i = 0;i < se->nb_fields; i++) { + field_types = thunk_convert(d + dst_offsets[i], + s + src_offsets[i], + field_types, to_host); + } + } + } + break; + default: + fprintf(stderr, "Invalid type 0x%x\n", type); + break; + } + return type_ptr; +} + +/* from em86 */ + +/* Utility function: Table-driven functions to translate bitmasks + * between X86 and Alpha formats... + */ +unsigned int target_to_host_bitmask(unsigned int x86_mask, + bitmask_transtbl * trans_tbl) +{ + bitmask_transtbl * btp; + unsigned int alpha_mask = 0; + + for(btp = trans_tbl; btp->x86_mask && btp->alpha_mask; btp++) { + if((x86_mask & btp->x86_mask) == btp->x86_bits) { + alpha_mask |= btp->alpha_bits; + } + } + return(alpha_mask); +} + +unsigned int host_to_target_bitmask(unsigned int alpha_mask, + bitmask_transtbl * trans_tbl) +{ + bitmask_transtbl * btp; + unsigned int x86_mask = 0; + + for(btp = trans_tbl; btp->x86_mask && btp->alpha_mask; btp++) { + if((alpha_mask & btp->alpha_mask) == btp->alpha_bits) { + x86_mask |= btp->x86_bits; + } + } + return(x86_mask); +} diff --git a/tools/ioemu/thunk.h b/tools/ioemu/thunk.h new file mode 100644 index 0000000000..42fd96f3a3 --- /dev/null +++ b/tools/ioemu/thunk.h @@ -0,0 +1,158 @@ +/* + * Generic thunking code to convert data between host and target CPU + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef THUNK_H +#define THUNK_H + +#include +#include "cpu.h" + +/* types enums definitions */ + +typedef enum argtype { + TYPE_NULL, + TYPE_CHAR, + TYPE_SHORT, + TYPE_INT, + TYPE_LONG, + TYPE_ULONG, + TYPE_PTRVOID, /* pointer on unknown data */ + TYPE_LONGLONG, + TYPE_ULONGLONG, + TYPE_PTR, + TYPE_ARRAY, + TYPE_STRUCT, +} argtype; + +#define MK_PTR(type) TYPE_PTR, type +#define MK_ARRAY(type, size) TYPE_ARRAY, size, type +#define MK_STRUCT(id) TYPE_STRUCT, id + +#define THUNK_TARGET 0 +#define THUNK_HOST 1 + +typedef struct { + /* standard struct handling */ + const argtype *field_types; + int nb_fields; + int *field_offsets[2]; + /* special handling */ + void (*convert[2])(void *dst, const void *src); + int size[2]; + int align[2]; + const char *name; +} StructEntry; + +/* Translation table for bitmasks... */ +typedef struct bitmask_transtbl { + unsigned int x86_mask; + unsigned int x86_bits; + unsigned int alpha_mask; + unsigned int alpha_bits; +} bitmask_transtbl; + +void thunk_register_struct(int id, const char *name, const argtype *types); +void thunk_register_struct_direct(int id, const char *name, StructEntry *se1); +const argtype *thunk_convert(void *dst, const void *src, + const argtype *type_ptr, int to_host); +#ifndef NO_THUNK_TYPE_SIZE + +extern StructEntry struct_entries[]; + +static inline int thunk_type_size(const argtype *type_ptr, int is_host) +{ + int type, size; + const StructEntry *se; + + type = *type_ptr; + switch(type) { + case TYPE_CHAR: + return 1; + case TYPE_SHORT: + return 2; + case TYPE_INT: + return 4; + case TYPE_LONGLONG: + case TYPE_ULONGLONG: + return 8; + case TYPE_LONG: + case TYPE_ULONG: + case TYPE_PTRVOID: + case TYPE_PTR: + if (is_host) { + return HOST_LONG_SIZE; + } else { + return TARGET_LONG_SIZE; + } + break; + case TYPE_ARRAY: + size = type_ptr[1]; + return size * thunk_type_size(type_ptr + 2, is_host); + case TYPE_STRUCT: + se = struct_entries + type_ptr[1]; + return se->size[is_host]; + default: + return -1; + } +} + +static inline int thunk_type_align(const argtype *type_ptr, int is_host) +{ + int type; + const StructEntry *se; + + type = *type_ptr; + switch(type) { + case TYPE_CHAR: + return 1; + case TYPE_SHORT: + return 2; + case TYPE_INT: + return 4; + case TYPE_LONGLONG: + case TYPE_ULONGLONG: + return 8; + case TYPE_LONG: + case TYPE_ULONG: + case TYPE_PTRVOID: + case TYPE_PTR: + if (is_host) { + return HOST_LONG_SIZE; + } else { + return TARGET_LONG_SIZE; + } + break; + case TYPE_ARRAY: + return thunk_type_align(type_ptr + 2, is_host); + case TYPE_STRUCT: + se = struct_entries + type_ptr[1]; + return se->align[is_host]; + default: + return -1; + } +} + +#endif /* NO_THUNK_TYPE_SIZE */ + +unsigned int target_to_host_bitmask(unsigned int x86_mask, + bitmask_transtbl * trans_tbl); +unsigned int host_to_target_bitmask(unsigned int alpha_mask, + bitmask_transtbl * trans_tbl); + +#endif diff --git a/tools/ioemu/translate-all.c b/tools/ioemu/translate-all.c new file mode 100644 index 0000000000..0de429f5b9 --- /dev/null +++ b/tools/ioemu/translate-all.c @@ -0,0 +1,311 @@ +/* + * Host code generation + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include + +#include "config.h" + +#define NO_CPU_IO_DEFS +#include "cpu.h" +#include "exec-all.h" +#include "disas.h" + +extern int dyngen_code(uint8_t *gen_code_buf, + uint16_t *label_offsets, uint16_t *jmp_offsets, + const uint16_t *opc_buf, const uint32_t *opparam_buf, const long *gen_labels); + +enum { +#define DEF(s, n, copy_size) INDEX_op_ ## s, +#include "opc.h" +#undef DEF + NB_OPS, +}; + +uint16_t gen_opc_buf[OPC_BUF_SIZE]; +uint32_t gen_opparam_buf[OPPARAM_BUF_SIZE]; +long gen_labels[OPC_BUF_SIZE]; +int nb_gen_labels; + +target_ulong gen_opc_pc[OPC_BUF_SIZE]; +uint8_t gen_opc_instr_start[OPC_BUF_SIZE]; +#if defined(TARGET_I386) +uint8_t gen_opc_cc_op[OPC_BUF_SIZE]; +#elif defined(TARGET_SPARC) +target_ulong gen_opc_npc[OPC_BUF_SIZE]; +target_ulong gen_opc_jump_pc[2]; +#elif defined(TARGET_MIPS) +uint32_t gen_opc_hflags[OPC_BUF_SIZE]; +#endif + +int code_copy_enabled = 1; + +#ifdef DEBUG_DISAS +static const char *op_str[] = { +#define DEF(s, n, copy_size) #s, +#include "opc.h" +#undef DEF +}; + +static uint8_t op_nb_args[] = { +#define DEF(s, n, copy_size) n, +#include "opc.h" +#undef DEF +}; + +static const unsigned short opc_copy_size[] = { +#define DEF(s, n, copy_size) copy_size, +#include "opc.h" +#undef DEF +}; + +void dump_ops(const uint16_t *opc_buf, const uint32_t *opparam_buf) +{ + const uint16_t *opc_ptr; + const uint32_t *opparam_ptr; + int c, n, i; + + opc_ptr = opc_buf; + opparam_ptr = opparam_buf; + for(;;) { + c = *opc_ptr++; + n = op_nb_args[c]; + fprintf(logfile, "0x%04x: %s", + (int)(opc_ptr - opc_buf - 1), op_str[c]); + for(i = 0; i < n; i++) { + fprintf(logfile, " 0x%x", opparam_ptr[i]); + } + fprintf(logfile, "\n"); + if (c == INDEX_op_end) + break; + opparam_ptr += n; + } +} + +#endif + +/* compute label info */ +static void dyngen_labels(long *gen_labels, int nb_gen_labels, + uint8_t *gen_code_buf, const uint16_t *opc_buf) +{ + uint8_t *gen_code_ptr; + int c, i; + unsigned long gen_code_addr[OPC_BUF_SIZE]; + + if (nb_gen_labels == 0) + return; + /* compute the address of each op code */ + + gen_code_ptr = gen_code_buf; + i = 0; + for(;;) { + c = opc_buf[i]; + gen_code_addr[i] =(unsigned long)gen_code_ptr; + if (c == INDEX_op_end) + break; + gen_code_ptr += opc_copy_size[c]; + i++; + } + + /* compute the address of each label */ + for(i = 0; i < nb_gen_labels; i++) { + gen_labels[i] = gen_code_addr[gen_labels[i]]; + } +} + +/* return non zero if the very first instruction is invalid so that + the virtual CPU can trigger an exception. + + '*gen_code_size_ptr' contains the size of the generated code (host + code). +*/ +int cpu_gen_code(CPUState *env, TranslationBlock *tb, + int max_code_size, int *gen_code_size_ptr) +{ + uint8_t *gen_code_buf; + int gen_code_size; + +#ifdef USE_CODE_COPY + if (code_copy_enabled && + cpu_gen_code_copy(env, tb, max_code_size, &gen_code_size) == 0) { + /* nothing more to do */ + } else +#endif + { + if (gen_intermediate_code(env, tb) < 0) + return -1; + + /* generate machine code */ + tb->tb_next_offset[0] = 0xffff; + tb->tb_next_offset[1] = 0xffff; + gen_code_buf = tb->tc_ptr; +#ifdef USE_DIRECT_JUMP + /* the following two entries are optional (only used for string ops) */ + tb->tb_jmp_offset[2] = 0xffff; + tb->tb_jmp_offset[3] = 0xffff; +#endif + dyngen_labels(gen_labels, nb_gen_labels, gen_code_buf, gen_opc_buf); + + gen_code_size = dyngen_code(gen_code_buf, tb->tb_next_offset, +#ifdef USE_DIRECT_JUMP + tb->tb_jmp_offset, +#else + NULL, +#endif + gen_opc_buf, gen_opparam_buf, gen_labels); + } + *gen_code_size_ptr = gen_code_size; +#ifdef DEBUG_DISAS + if (loglevel & CPU_LOG_TB_OUT_ASM) { + fprintf(logfile, "OUT: [size=%d]\n", *gen_code_size_ptr); + disas(logfile, tb->tc_ptr, *gen_code_size_ptr); + fprintf(logfile, "\n"); + fflush(logfile); + } +#endif + return 0; +} + +/* The cpu state corresponding to 'searched_pc' is restored. + */ +int cpu_restore_state(TranslationBlock *tb, + CPUState *env, unsigned long searched_pc, + void *puc) +{ + int j, c; + unsigned long tc_ptr; + uint16_t *opc_ptr; + +#ifdef USE_CODE_COPY + if (tb->cflags & CF_CODE_COPY) { + return cpu_restore_state_copy(tb, env, searched_pc, puc); + } +#endif + if (gen_intermediate_code_pc(env, tb) < 0) + return -1; + + /* find opc index corresponding to search_pc */ + tc_ptr = (unsigned long)tb->tc_ptr; + if (searched_pc < tc_ptr) + return -1; + j = 0; + opc_ptr = gen_opc_buf; + for(;;) { + c = *opc_ptr; + if (c == INDEX_op_end) + return -1; + tc_ptr += opc_copy_size[c]; + if (searched_pc < tc_ptr) + break; + opc_ptr++; + } + j = opc_ptr - gen_opc_buf; + /* now find start of instruction before */ + while (gen_opc_instr_start[j] == 0) + j--; +#if defined(TARGET_I386) + { + int cc_op; +#ifdef DEBUG_DISAS + if (loglevel & CPU_LOG_TB_OP) { + int i; + fprintf(logfile, "RESTORE:\n"); + for(i=0;i<=j; i++) { + if (gen_opc_instr_start[i]) { + fprintf(logfile, "0x%04x: " TARGET_FMT_lx "\n", i, gen_opc_pc[i]); + } + } + fprintf(logfile, "spc=0x%08lx j=0x%x eip=" TARGET_FMT_lx " cs_base=%x\n", + searched_pc, j, gen_opc_pc[j] - tb->cs_base, + (uint32_t)tb->cs_base); + } +#endif + env->eip = gen_opc_pc[j] - tb->cs_base; + cc_op = gen_opc_cc_op[j]; + if (cc_op != CC_OP_DYNAMIC) + env->cc_op = cc_op; + } +#elif defined(TARGET_ARM) + env->regs[15] = gen_opc_pc[j]; +#elif defined(TARGET_SPARC) + { + target_ulong npc; + env->pc = gen_opc_pc[j]; + npc = gen_opc_npc[j]; + if (npc == 1) { + /* dynamic NPC: already stored */ + } else if (npc == 2) { + target_ulong t2 = (target_ulong)puc; + /* jump PC: use T2 and the jump targets of the translation */ + if (t2) + env->npc = gen_opc_jump_pc[0]; + else + env->npc = gen_opc_jump_pc[1]; + } else { + env->npc = npc; + } + } +#elif defined(TARGET_PPC) + { + int type; + /* for PPC, we need to look at the micro operation to get the + access type */ + env->nip = gen_opc_pc[j]; + switch(c) { +#if defined(CONFIG_USER_ONLY) +#define CASE3(op)\ + case INDEX_op_ ## op ## _raw +#else +#define CASE3(op)\ + case INDEX_op_ ## op ## _user:\ + case INDEX_op_ ## op ## _kernel +#endif + + CASE3(stfd): + CASE3(stfs): + CASE3(lfd): + CASE3(lfs): + type = ACCESS_FLOAT; + break; + CASE3(lwarx): + type = ACCESS_RES; + break; + CASE3(stwcx): + type = ACCESS_RES; + break; + CASE3(eciwx): + CASE3(ecowx): + type = ACCESS_EXT; + break; + default: + type = ACCESS_INT; + break; + } + env->access_type = type; + } +#elif defined(TARGET_MIPS) + env->PC = gen_opc_pc[j]; + env->hflags &= ~MIPS_HFLAG_BMASK; + env->hflags |= gen_opc_hflags[j]; +#endif + return 0; +} diff --git a/tools/ioemu/translate-op.c b/tools/ioemu/translate-op.c new file mode 100644 index 0000000000..fddac70c4a --- /dev/null +++ b/tools/ioemu/translate-op.c @@ -0,0 +1,37 @@ +/* + * Host code generation + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include + +#include "config.h" + +enum { +#define DEF(s, n, copy_size) INDEX_op_ ## s, +#include "opc.h" +#undef DEF + NB_OPS, +}; + +#include "dyngen.h" +#include "op.h" + diff --git a/tools/ioemu/usb-linux.c b/tools/ioemu/usb-linux.c new file mode 100644 index 0000000000..b08eaebabb --- /dev/null +++ b/tools/ioemu/usb-linux.c @@ -0,0 +1,488 @@ +/* + * Linux host USB redirector + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#if defined(__linux__) +#include +#include +#define __user /* new versions of usbdevice_fs.h use this private attribute */ +#include +#include + +/* We redefine it to avoid version problems */ +struct usb_ctrltransfer { + uint8_t bRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + uint32_t timeout; + void *data; +}; + +typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id, + int vendor_id, int product_id, + const char *product_name, int speed); +static int usb_host_find_device(int *pbus_num, int *paddr, + const char *devname); + +//#define DEBUG + +#define USBDEVFS_PATH "/proc/bus/usb" + +typedef struct USBHostDevice { + USBDevice dev; + int fd; +} USBHostDevice; + +static void usb_host_handle_reset(USBDevice *dev) +{ +#if 0 + USBHostDevice *s = (USBHostDevice *)dev; + /* USBDEVFS_RESET, but not the first time as it has already be + done by the host OS */ + ioctl(s->fd, USBDEVFS_RESET); +#endif +} + +static int usb_host_handle_control(USBDevice *dev, + int request, + int value, + int index, + int length, + uint8_t *data) +{ + USBHostDevice *s = (USBHostDevice *)dev; + struct usb_ctrltransfer ct; + int ret; + + if (request == (DeviceOutRequest | USB_REQ_SET_ADDRESS)) { + /* specific SET_ADDRESS support */ + dev->addr = value; + return 0; + } else { + ct.bRequestType = request >> 8; + ct.bRequest = request; + ct.wValue = value; + ct.wIndex = index; + ct.wLength = length; + ct.timeout = 50; + ct.data = data; + ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct); + if (ret < 0) { + switch(errno) { + case ETIMEDOUT: + return USB_RET_NAK; + default: + return USB_RET_STALL; + } + } else { + return ret; + } + } +} + +static int usb_host_handle_data(USBDevice *dev, int pid, + uint8_t devep, + uint8_t *data, int len) +{ + USBHostDevice *s = (USBHostDevice *)dev; + struct usbdevfs_bulktransfer bt; + int ret; + + /* XXX: optimize and handle all data types by looking at the + config descriptor */ + if (pid == USB_TOKEN_IN) + devep |= 0x80; + bt.ep = devep; + bt.len = len; + bt.timeout = 50; + bt.data = data; + ret = ioctl(s->fd, USBDEVFS_BULK, &bt); + if (ret < 0) { + switch(errno) { + case ETIMEDOUT: + return USB_RET_NAK; + case EPIPE: + default: +#ifdef DEBUG + printf("handle_data: errno=%d\n", errno); +#endif + return USB_RET_STALL; + } + } else { + return ret; + } +} + +/* XXX: exclude high speed devices or implement EHCI */ +USBDevice *usb_host_device_open(const char *devname) +{ + int fd, interface, ret, i; + USBHostDevice *dev; + struct usbdevfs_connectinfo ci; + uint8_t descr[1024]; + char buf[1024]; + int descr_len, dev_descr_len, config_descr_len, nb_interfaces; + int bus_num, addr; + + if (usb_host_find_device(&bus_num, &addr, devname) < 0) + return NULL; + + snprintf(buf, sizeof(buf), USBDEVFS_PATH "/%03d/%03d", + bus_num, addr); + fd = open(buf, O_RDWR); + if (fd < 0) { + perror(buf); + return NULL; + } + + /* read the config description */ + descr_len = read(fd, descr, sizeof(descr)); + if (descr_len <= 0) { + perror("read descr"); + goto fail; + } + + i = 0; + dev_descr_len = descr[0]; + if (dev_descr_len > descr_len) + goto fail; + i += dev_descr_len; + config_descr_len = descr[i]; + if (i + config_descr_len > descr_len) + goto fail; + nb_interfaces = descr[i + 4]; + if (nb_interfaces != 1) { + /* NOTE: currently we grab only one interface */ + fprintf(stderr, "usb_host: only one interface supported\n"); + goto fail; + } + +#ifdef USBDEVFS_DISCONNECT + /* earlier Linux 2.4 do not support that */ + { + struct usbdevfs_ioctl ctrl; + ctrl.ioctl_code = USBDEVFS_DISCONNECT; + ctrl.ifno = 0; + ret = ioctl(fd, USBDEVFS_IOCTL, &ctrl); + if (ret < 0 && errno != ENODATA) { + perror("USBDEVFS_DISCONNECT"); + goto fail; + } + } +#endif + + /* XXX: only grab if all interfaces are free */ + interface = 0; + ret = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &interface); + if (ret < 0) { + if (errno == EBUSY) { + fprintf(stderr, "usb_host: device already grabbed\n"); + } else { + perror("USBDEVFS_CLAIMINTERFACE"); + } + fail: + close(fd); + return NULL; + } + + ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci); + if (ret < 0) { + perror("USBDEVFS_CONNECTINFO"); + goto fail; + } + +#ifdef DEBUG + printf("host USB device %d.%d grabbed\n", bus_num, addr); +#endif + + dev = qemu_mallocz(sizeof(USBHostDevice)); + if (!dev) + goto fail; + dev->fd = fd; + if (ci.slow) + dev->dev.speed = USB_SPEED_LOW; + else + dev->dev.speed = USB_SPEED_HIGH; + dev->dev.handle_packet = usb_generic_handle_packet; + + dev->dev.handle_reset = usb_host_handle_reset; + dev->dev.handle_control = usb_host_handle_control; + dev->dev.handle_data = usb_host_handle_data; + return (USBDevice *)dev; +} + +static int get_tag_value(char *buf, int buf_size, + const char *str, const char *tag, + const char *stopchars) +{ + const char *p; + char *q; + p = strstr(str, tag); + if (!p) + return -1; + p += strlen(tag); + while (isspace(*p)) + p++; + q = buf; + while (*p != '\0' && !strchr(stopchars, *p)) { + if ((q - buf) < (buf_size - 1)) + *q++ = *p; + p++; + } + *q = '\0'; + return q - buf; +} + +static int usb_host_scan(void *opaque, USBScanFunc *func) +{ + FILE *f; + char line[1024]; + char buf[1024]; + int bus_num, addr, speed, device_count, class_id, product_id, vendor_id; + int ret; + char product_name[512]; + + f = fopen(USBDEVFS_PATH "/devices", "r"); + if (!f) { + term_printf("Could not open %s\n", USBDEVFS_PATH "/devices"); + return 0; + } + device_count = 0; + bus_num = addr = speed = class_id = product_id = vendor_id = 0; + ret = 0; + for(;;) { + if (fgets(line, sizeof(line), f) == NULL) + break; + if (strlen(line) > 0) + line[strlen(line) - 1] = '\0'; + if (line[0] == 'T' && line[1] == ':') { + if (device_count && (vendor_id || product_id)) { + /* New device. Add the previously discovered device. */ + ret = func(opaque, bus_num, addr, class_id, vendor_id, + product_id, product_name, speed); + if (ret) + goto the_end; + } + if (get_tag_value(buf, sizeof(buf), line, "Bus=", " ") < 0) + goto fail; + bus_num = atoi(buf); + if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0) + goto fail; + addr = atoi(buf); + if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0) + goto fail; + if (!strcmp(buf, "480")) + speed = USB_SPEED_HIGH; + else if (!strcmp(buf, "1.5")) + speed = USB_SPEED_LOW; + else + speed = USB_SPEED_FULL; + product_name[0] = '\0'; + class_id = 0xff; + device_count++; + product_id = 0; + vendor_id = 0; + } else if (line[0] == 'P' && line[1] == ':') { + if (get_tag_value(buf, sizeof(buf), line, "Vendor=", " ") < 0) + goto fail; + vendor_id = strtoul(buf, NULL, 16); + if (get_tag_value(buf, sizeof(buf), line, "ProdID=", " ") < 0) + goto fail; + product_id = strtoul(buf, NULL, 16); + } else if (line[0] == 'S' && line[1] == ':') { + if (get_tag_value(buf, sizeof(buf), line, "Product=", "") < 0) + goto fail; + pstrcpy(product_name, sizeof(product_name), buf); + } else if (line[0] == 'D' && line[1] == ':') { + if (get_tag_value(buf, sizeof(buf), line, "Cls=", " (") < 0) + goto fail; + class_id = strtoul(buf, NULL, 16); + } + fail: ; + } + if (device_count && (vendor_id || product_id)) { + /* Add the last device. */ + ret = func(opaque, bus_num, addr, class_id, vendor_id, + product_id, product_name, speed); + } + the_end: + fclose(f); + return ret; +} + +typedef struct FindDeviceState { + int vendor_id; + int product_id; + int bus_num; + int addr; +} FindDeviceState; + +static int usb_host_find_device_scan(void *opaque, int bus_num, int addr, + int class_id, + int vendor_id, int product_id, + const char *product_name, int speed) +{ + FindDeviceState *s = opaque; + if (vendor_id == s->vendor_id && + product_id == s->product_id) { + s->bus_num = bus_num; + s->addr = addr; + return 1; + } else { + return 0; + } +} + +/* the syntax is : + 'bus.addr' (decimal numbers) or + 'vendor_id:product_id' (hexa numbers) */ +static int usb_host_find_device(int *pbus_num, int *paddr, + const char *devname) +{ + const char *p; + int ret; + FindDeviceState fs; + + p = strchr(devname, '.'); + if (p) { + *pbus_num = strtoul(devname, NULL, 0); + *paddr = strtoul(p + 1, NULL, 0); + return 0; + } + p = strchr(devname, ':'); + if (p) { + fs.vendor_id = strtoul(devname, NULL, 16); + fs.product_id = strtoul(p + 1, NULL, 16); + ret = usb_host_scan(&fs, usb_host_find_device_scan); + if (ret) { + *pbus_num = fs.bus_num; + *paddr = fs.addr; + return 0; + } + } + return -1; +} + +/**********************/ +/* USB host device info */ + +struct usb_class_info { + int class; + const char *class_name; +}; + +static const struct usb_class_info usb_class_info[] = { + { USB_CLASS_AUDIO, "Audio"}, + { USB_CLASS_COMM, "Communication"}, + { USB_CLASS_HID, "HID"}, + { USB_CLASS_HUB, "Hub" }, + { USB_CLASS_PHYSICAL, "Physical" }, + { USB_CLASS_PRINTER, "Printer" }, + { USB_CLASS_MASS_STORAGE, "Storage" }, + { USB_CLASS_CDC_DATA, "Data" }, + { USB_CLASS_APP_SPEC, "Application Specific" }, + { USB_CLASS_VENDOR_SPEC, "Vendor Specific" }, + { USB_CLASS_STILL_IMAGE, "Still Image" }, + { USB_CLASS_CSCID, "Smart Card" }, + { USB_CLASS_CONTENT_SEC, "Content Security" }, + { -1, NULL } +}; + +static const char *usb_class_str(uint8_t class) +{ + const struct usb_class_info *p; + for(p = usb_class_info; p->class != -1; p++) { + if (p->class == class) + break; + } + return p->class_name; +} + +void usb_info_device(int bus_num, int addr, int class_id, + int vendor_id, int product_id, + const char *product_name, + int speed) +{ + const char *class_str, *speed_str; + + switch(speed) { + case USB_SPEED_LOW: + speed_str = "1.5"; + break; + case USB_SPEED_FULL: + speed_str = "12"; + break; + case USB_SPEED_HIGH: + speed_str = "480"; + break; + default: + speed_str = "?"; + break; + } + + term_printf(" Device %d.%d, speed %s Mb/s\n", + bus_num, addr, speed_str); + class_str = usb_class_str(class_id); + if (class_str) + term_printf(" %s:", class_str); + else + term_printf(" Class %02x:", class_id); + term_printf(" USB device %04x:%04x", vendor_id, product_id); + if (product_name[0] != '\0') + term_printf(", %s", product_name); + term_printf("\n"); +} + +static int usb_host_info_device(void *opaque, int bus_num, int addr, + int class_id, + int vendor_id, int product_id, + const char *product_name, + int speed) +{ + usb_info_device(bus_num, addr, class_id, vendor_id, product_id, + product_name, speed); + return 0; +} + +void usb_host_info(void) +{ + usb_host_scan(NULL, usb_host_info_device); +} + +#else + +void usb_host_info(void) +{ + term_printf("USB host devices not supported\n"); +} + +/* XXX: modify configure to compile the right host driver */ +USBDevice *usb_host_device_open(const char *devname) +{ + return NULL; +} + +#endif diff --git a/tools/ioemu/vgafont.h b/tools/ioemu/vgafont.h new file mode 100644 index 0000000000..bb75796be5 --- /dev/null +++ b/tools/ioemu/vgafont.h @@ -0,0 +1,4611 @@ +static uint8_t vgafont16[256 * 16] = { + + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 1 0x01 '^A' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x81, /* 10000001 */ + 0xa5, /* 10100101 */ + 0x81, /* 10000001 */ + 0x81, /* 10000001 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0x81, /* 10000001 */ + 0x81, /* 10000001 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 2 0x02 '^B' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xdb, /* 11011011 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 3 0x03 '^C' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 4 0x04 '^D' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 5 0x05 '^E' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 6 0x06 '^F' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 7 0x07 '^G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 8 0x08 '^H' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xe7, /* 11100111 */ + 0xc3, /* 11000011 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 9 0x09 '^I' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x42, /* 01000010 */ + 0x42, /* 01000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 10 0x0a '^J' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0x99, /* 10011001 */ + 0xbd, /* 10111101 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0xc3, /* 11000011 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 11 0x0b '^K' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x0e, /* 00001110 */ + 0x1a, /* 00011010 */ + 0x32, /* 00110010 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 12 0x0c '^L' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 13 0x0d '^M' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x33, /* 00110011 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x70, /* 01110000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 14 0x0e '^N' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x67, /* 01100111 */ + 0xe7, /* 11100111 */ + 0xe6, /* 11100110 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 15 0x0f '^O' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xdb, /* 11011011 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0x3c, /* 00111100 */ + 0xdb, /* 11011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 16 0x10 '^P' */ + 0x00, /* 00000000 */ + 0x80, /* 10000000 */ + 0xc0, /* 11000000 */ + 0xe0, /* 11100000 */ + 0xf0, /* 11110000 */ + 0xf8, /* 11111000 */ + 0xfe, /* 11111110 */ + 0xf8, /* 11111000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 17 0x11 '^Q' */ + 0x00, /* 00000000 */ + 0x02, /* 00000010 */ + 0x06, /* 00000110 */ + 0x0e, /* 00001110 */ + 0x1e, /* 00011110 */ + 0x3e, /* 00111110 */ + 0xfe, /* 11111110 */ + 0x3e, /* 00111110 */ + 0x1e, /* 00011110 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 18 0x12 '^R' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 19 0x13 '^S' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 20 0x14 '^T' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7f, /* 01111111 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7b, /* 01111011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 21 0x15 '^U' */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 22 0x16 '^V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 23 0x17 '^W' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 24 0x18 '^X' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 25 0x19 '^Y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 26 0x1a '^Z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 27 0x1b '^[' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 28 0x1c '^\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 29 0x1d '^]' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x28, /* 00101000 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x28, /* 00101000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 30 0x1e '^^' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 31 0x1f '^_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 32 0x20 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 33 0x21 '!' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 34 0x22 '"' */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 35 0x23 '#' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 36 0x24 '$' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x86, /* 10000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 37 0x25 '%' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc2, /* 11000010 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0x86, /* 10000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 38 0x26 '&' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 39 0x27 ''' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 40 0x28 '(' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 41 0x29 ')' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 42 0x2a '*' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0xff, /* 11111111 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 43 0x2b '+' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 44 0x2c ',' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 45 0x2d '-' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 46 0x2e '.' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 47 0x2f '/' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x02, /* 00000010 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 48 0x30 '0' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 49 0x31 '1' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x38, /* 00111000 */ + 0x78, /* 01111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 50 0x32 '2' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 51 0x33 '3' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 52 0x34 '4' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x1c, /* 00011100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 53 0x35 '5' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 54 0x36 '6' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 55 0x37 '7' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 56 0x38 '8' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 57 0x39 '9' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 58 0x3a ':' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 59 0x3b ';' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 60 0x3c '<' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 61 0x3d '=' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 62 0x3e '>' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 63 0x3f '?' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 64 0x40 '@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xdc, /* 11011100 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 65 0x41 'A' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 66 0x42 'B' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 67 0x43 'C' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc2, /* 11000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 68 0x44 'D' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 69 0x45 'E' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 70 0x46 'F' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 71 0x47 'G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xde, /* 11011110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x66, /* 01100110 */ + 0x3a, /* 00111010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 72 0x48 'H' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 73 0x49 'I' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 74 0x4a 'J' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 75 0x4b 'K' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe6, /* 11100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 76 0x4c 'L' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 77 0x4d 'M' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xee, /* 11101110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 78 0x4e 'N' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xfe, /* 11111110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 79 0x4f 'O' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 80 0x50 'P' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 81 0x51 'Q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xde, /* 11011110 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 82 0x52 'R' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 83 0x53 'S' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 84 0x54 'T' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x5a, /* 01011010 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 85 0x55 'U' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 86 0x56 'V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 87 0x57 'W' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0xee, /* 11101110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 88 0x58 'X' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 89 0x59 'Y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 90 0x5a 'Z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x86, /* 10000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc2, /* 11000010 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 91 0x5b '[' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 92 0x5c '\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x80, /* 10000000 */ + 0xc0, /* 11000000 */ + 0xe0, /* 11100000 */ + 0x70, /* 01110000 */ + 0x38, /* 00111000 */ + 0x1c, /* 00011100 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 93 0x5d ']' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 94 0x5e '^' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 96 0x60 '`' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 98 0x62 'b' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 100 0x64 'd' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 102 0x66 'f' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x36, /* 00110110 */ + 0x32, /* 00110010 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 104 0x68 'h' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x6c, /* 01101100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 105 0x69 'i' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 106 0x6a 'j' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 107 0x6b 'k' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 108 0x6c 'l' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 113 0x71 'q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* 114 0x72 'r' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 115 0x73 's' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 116 0x74 't' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0xfc, /* 11111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x36, /* 00110110 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + + /* 122 0x7a 'z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 123 0x7b '{' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 124 0x7c '|' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 125 0x7d '}' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 126 0x7e '~' */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 127 0x7f '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 128 0x80 '€' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc2, /* 11000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 129 0x81 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 130 0x82 '‚' */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 131 0x83 'ƒ' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 132 0x84 '„' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 133 0x85 '…' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 134 0x86 '†' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 135 0x87 '‡' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 136 0x88 'ˆ' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 137 0x89 '‰' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 138 0x8a 'Š' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 139 0x8b '‹' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 140 0x8c 'Œ' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 141 0x8d '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 142 0x8e 'Ž' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 143 0x8f '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 144 0x90 '' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 145 0x91 '‘' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x6e, /* 01101110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 146 0x92 '’' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3e, /* 00111110 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 147 0x93 '“' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 148 0x94 '”' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 149 0x95 '•' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 150 0x96 '–' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 151 0x97 '—' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 152 0x98 '˜' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 153 0x99 '™' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 154 0x9a 'š' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 155 0x9b '›' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 156 0x9c 'œ' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x64, /* 01100100 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xe6, /* 11100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 157 0x9d '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 158 0x9e 'ž' */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xf8, /* 11111000 */ + 0xc4, /* 11000100 */ + 0xcc, /* 11001100 */ + 0xde, /* 11011110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 159 0x9f 'Ÿ' */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 160 0xa0 ' ' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 161 0xa1 '¡' */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 162 0xa2 '¢' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 163 0xa3 '£' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 164 0xa4 '¤' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 165 0xa5 'Â¥' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xfe, /* 11111110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 166 0xa6 '¦' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 167 0xa7 '§' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 168 0xa8 '¨' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 169 0xa9 '©' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 170 0xaa 'ª' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 171 0xab '«' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0xe0, /* 11100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xdc, /* 11011100 */ + 0x86, /* 10000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 172 0xac '¬' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0xe0, /* 11100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xce, /* 11001110 */ + 0x9a, /* 10011010 */ + 0x3f, /* 00111111 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 173 0xad '­' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 174 0xae '®' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x36, /* 00110110 */ + 0x6c, /* 01101100 */ + 0xd8, /* 11011000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 175 0xaf '¯' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xd8, /* 11011000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x6c, /* 01101100 */ + 0xd8, /* 11011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 176 0xb0 '°' */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + + /* 177 0xb1 '±' */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + + /* 178 0xb2 '²' */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + + /* 179 0xb3 '³' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 180 0xb4 '´' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 181 0xb5 'µ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 182 0xb6 '¶' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 183 0xb7 '·' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 184 0xb8 '¸' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 185 0xb9 '¹' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 186 0xba 'º' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 187 0xbb '»' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 188 0xbc '¼' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 189 0xbd '½' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 190 0xbe '¾' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 191 0xbf '¿' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 192 0xc0 'À' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 193 0xc1 'Á' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 194 0xc2 'Â' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 195 0xc3 'Ã' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 196 0xc4 'Ä' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 197 0xc5 'Å' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 198 0xc6 'Æ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 199 0xc7 'Ç' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 200 0xc8 'È' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 201 0xc9 'É' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 202 0xca 'Ê' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 203 0xcb 'Ë' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 204 0xcc 'Ì' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 205 0xcd 'Í' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 206 0xce 'Î' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 207 0xcf 'Ï' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 208 0xd0 'Ð' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 209 0xd1 'Ñ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 210 0xd2 'Ò' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 211 0xd3 'Ó' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 212 0xd4 'Ô' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 213 0xd5 'Õ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 214 0xd6 'Ö' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 215 0xd7 '×' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 216 0xd8 'Ø' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 217 0xd9 'Ù' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 218 0xda 'Ú' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 219 0xdb 'Û' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 220 0xdc 'Ü' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 221 0xdd 'Ý' */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + + /* 222 0xde 'Þ' */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + + /* 223 0xdf 'ß' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 224 0xe0 'à' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 225 0xe1 'á' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xd8, /* 11011000 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 226 0xe2 'â' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 227 0xe3 'ã' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 228 0xe4 'ä' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 229 0xe5 'Ã¥' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 230 0xe6 'æ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* 231 0xe7 'ç' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 232 0xe8 'è' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 233 0xe9 'é' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 234 0xea 'ê' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xee, /* 11101110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 235 0xeb 'ë' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x3e, /* 00111110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 236 0xec 'ì' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 237 0xed 'í' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x03, /* 00000011 */ + 0x06, /* 00000110 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xf3, /* 11110011 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 238 0xee 'î' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 239 0xef 'ï' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 240 0xf0 'ð' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 241 0xf1 'ñ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 242 0xf2 'ò' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 243 0xf3 'ó' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 244 0xf4 'ô' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 245 0xf5 'õ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 246 0xf6 'ö' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 247 0xf7 '÷' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 248 0xf8 'ø' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 249 0xf9 'ù' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 250 0xfa 'ú' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 251 0xfb 'û' */ + 0x00, /* 00000000 */ + 0x0f, /* 00001111 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xec, /* 11101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3c, /* 00111100 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 252 0xfc 'ü' */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 253 0xfd 'ý' */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 254 0xfe 'þ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 255 0xff 'ÿ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + +}; diff --git a/tools/ioemu/vl.c b/tools/ioemu/vl.c new file mode 100644 index 0000000000..e9dc632746 --- /dev/null +++ b/tools/ioemu/vl.c @@ -0,0 +1,6001 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _BSD +#include +#ifndef __APPLE__ +#include +#endif +#else +#ifndef __sun__ +#include +#include +#include +#include +#include +#include +#endif +#endif +#endif + +#if defined(CONFIG_SLIRP) +#include "libslirp.h" +#endif + +#ifdef _WIN32 +#include +#include +#include +#define getopt_long_only getopt_long +#define memalign(align, size) malloc(size) +#endif + +#include "qemu_socket.h" + +#ifdef CONFIG_SDL +#ifdef __APPLE__ +#include +#endif +#endif /* CONFIG_SDL */ + +#ifdef CONFIG_COCOA +#undef main +#define main qemu_main +#endif /* CONFIG_COCOA */ + +#include "disas.h" + +#include "exec-all.h" + +#define DEFAULT_NETWORK_SCRIPT "/etc/xen/qemu-ifup" +#define DEFAULT_BRIDGE "xenbr0" + +//#define DEBUG_UNUSED_IOPORT +//#define DEBUG_IOPORT + +#if !defined(CONFIG_SOFTMMU) +#define PHYS_RAM_MAX_SIZE (256 * 1024 * 1024) +#else +#define PHYS_RAM_MAX_SIZE (2047 * 1024 * 1024) +#endif + +#ifdef TARGET_PPC +#define DEFAULT_RAM_SIZE 144 +#else +#define DEFAULT_RAM_SIZE 128 +#endif +/* in ms */ +#define GUI_REFRESH_INTERVAL 30 + +/* XXX: use a two level table to limit memory usage */ +#define MAX_IOPORTS 65536 + +const char *bios_dir = CONFIG_QEMU_SHAREDIR; +char phys_ram_file[1024]; +void *ioport_opaque[MAX_IOPORTS]; +IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS]; +IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; +BlockDriverState *bs_table[MAX_DISKS], *fd_table[MAX_FD]; +int vga_ram_size; +int bios_size; +static DisplayState display_state; +int nographic; +const char* keyboard_layout = NULL; +int64_t ticks_per_sec; +int boot_device = 'c'; +uint64_t ram_size; +int pit_min_timer_count = 0; +int nb_nics; +NICInfo nd_table[MAX_NICS]; +QEMUTimer *gui_timer; +int vm_running; +int rtc_utc = 1; +int cirrus_vga_enabled = 1; +#ifdef TARGET_SPARC +int graphic_width = 1024; +int graphic_height = 768; +#else +int graphic_width = 800; +int graphic_height = 600; +#endif +int graphic_depth = 15; +int full_screen = 0; +CharDriverState *serial_hds[MAX_SERIAL_PORTS]; +CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; +#ifdef TARGET_I386 +int win2k_install_hack = 0; +#endif +int usb_enabled = 0; +USBPort *vm_usb_ports[MAX_VM_USB_PORTS]; +USBDevice *vm_usb_hub; +static VLANState *first_vlan; +int smp_cpus = 1; +int vnc_display = -1; +#if defined(TARGET_SPARC) +#define MAX_CPUS 16 +#elif defined(TARGET_I386) +#define MAX_CPUS 255 +#else +#define MAX_CPUS 1 +#endif + +extern int vcpus; + +int xc_handle; + +time_t timeoffset = 0; + +char domain_name[1024] = { 'H','V', 'M', 'X', 'E', 'N', '-'}; +extern int domid; + +/***********************************************************/ +/* x86 ISA bus support */ + +target_phys_addr_t isa_mem_base = 0; +PicState2 *isa_pic; + +uint32_t default_ioport_readb(void *opaque, uint32_t address) +{ +#ifdef DEBUG_UNUSED_IOPORT + fprintf(stderr, "inb: port=0x%04x\n", address); +#endif + return 0xff; +} + +void default_ioport_writeb(void *opaque, uint32_t address, uint32_t data) +{ +#ifdef DEBUG_UNUSED_IOPORT + fprintf(stderr, "outb: port=0x%04x data=0x%02x\n", address, data); +#endif +} + +/* default is to make two byte accesses */ +uint32_t default_ioport_readw(void *opaque, uint32_t address) +{ + uint32_t data; + data = ioport_read_table[0][address](ioport_opaque[address], address); + address = (address + 1) & (MAX_IOPORTS - 1); + data |= ioport_read_table[0][address](ioport_opaque[address], address) << 8; + return data; +} + +void default_ioport_writew(void *opaque, uint32_t address, uint32_t data) +{ + ioport_write_table[0][address](ioport_opaque[address], address, data & 0xff); + address = (address + 1) & (MAX_IOPORTS - 1); + ioport_write_table[0][address](ioport_opaque[address], address, (data >> 8) & 0xff); +} + +uint32_t default_ioport_readl(void *opaque, uint32_t address) +{ +#ifdef DEBUG_UNUSED_IOPORT + fprintf(stderr, "inl: port=0x%04x\n", address); +#endif + return 0xffffffff; +} + +void default_ioport_writel(void *opaque, uint32_t address, uint32_t data) +{ +#ifdef DEBUG_UNUSED_IOPORT + fprintf(stderr, "outl: port=0x%04x data=0x%02x\n", address, data); +#endif +} + +void init_ioports(void) +{ + int i; + + for(i = 0; i < MAX_IOPORTS; i++) { + ioport_read_table[0][i] = default_ioport_readb; + ioport_write_table[0][i] = default_ioport_writeb; + ioport_read_table[1][i] = default_ioport_readw; + ioport_write_table[1][i] = default_ioport_writew; + ioport_read_table[2][i] = default_ioport_readl; + ioport_write_table[2][i] = default_ioport_writel; + } +} + +/* size is the word size in byte */ +int register_ioport_read(int start, int length, int size, + IOPortReadFunc *func, void *opaque) +{ + int i, bsize; + + if (size == 1) { + bsize = 0; + } else if (size == 2) { + bsize = 1; + } else if (size == 4) { + bsize = 2; + } else { + hw_error("register_ioport_read: invalid size"); + return -1; + } + for(i = start; i < start + length; i += size) { + ioport_read_table[bsize][i] = func; + if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque) + hw_error("register_ioport_read: invalid opaque"); + ioport_opaque[i] = opaque; + } + return 0; +} + +/* size is the word size in byte */ +int register_ioport_write(int start, int length, int size, + IOPortWriteFunc *func, void *opaque) +{ + int i, bsize; + + if (size == 1) { + bsize = 0; + } else if (size == 2) { + bsize = 1; + } else if (size == 4) { + bsize = 2; + } else { + hw_error("register_ioport_write: invalid size"); + return -1; + } + for(i = start; i < start + length; i += size) { + ioport_write_table[bsize][i] = func; + if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque) + hw_error("register_ioport_read: invalid opaque"); + ioport_opaque[i] = opaque; + } + return 0; +} + +void isa_unassign_ioport(int start, int length) +{ + int i; + + for(i = start; i < start + length; i++) { + ioport_read_table[0][i] = default_ioport_readb; + ioport_read_table[1][i] = default_ioport_readw; + ioport_read_table[2][i] = default_ioport_readl; + + ioport_write_table[0][i] = default_ioport_writeb; + ioport_write_table[1][i] = default_ioport_writew; + ioport_write_table[2][i] = default_ioport_writel; + } +} + +/***********************************************************/ + +void pstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + if (buf_size <= 0) + return; + + for(;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = c; + } + *q = '\0'; +} + +/* strcat and truncate. */ +char *pstrcat(char *buf, int buf_size, const char *s) +{ + int len; + len = strlen(buf); + if (len < buf_size) + pstrcpy(buf + len, buf_size - len, s); + return buf; +} + +int strstart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (*p != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +void cpu_outb(CPUState *env, int addr, int val) +{ +#ifdef DEBUG_IOPORT + if (loglevel & CPU_LOG_IOPORT) + fprintf(logfile, "outb: %04x %02x\n", addr, val); +#endif + ioport_write_table[0][addr](ioport_opaque[addr], addr, val); +#ifdef USE_KQEMU + if (env) + env->last_io_time = cpu_get_time_fast(); +#endif +} + +void cpu_outw(CPUState *env, int addr, int val) +{ +#ifdef DEBUG_IOPORT + if (loglevel & CPU_LOG_IOPORT) + fprintf(logfile, "outw: %04x %04x\n", addr, val); +#endif + ioport_write_table[1][addr](ioport_opaque[addr], addr, val); +#ifdef USE_KQEMU + if (env) + env->last_io_time = cpu_get_time_fast(); +#endif +} + +void cpu_outl(CPUState *env, int addr, int val) +{ +#ifdef DEBUG_IOPORT + if (loglevel & CPU_LOG_IOPORT) + fprintf(logfile, "outl: %04x %08x\n", addr, val); +#endif + ioport_write_table[2][addr](ioport_opaque[addr], addr, val); +#ifdef USE_KQEMU + if (env) + env->last_io_time = cpu_get_time_fast(); +#endif +} + +int cpu_inb(CPUState *env, int addr) +{ + int val; + val = ioport_read_table[0][addr](ioport_opaque[addr], addr); +#ifdef DEBUG_IOPORT + if (loglevel & CPU_LOG_IOPORT) + fprintf(logfile, "inb : %04x %02x\n", addr, val); +#endif +#ifdef USE_KQEMU + if (env) + env->last_io_time = cpu_get_time_fast(); +#endif + return val; +} + +int cpu_inw(CPUState *env, int addr) +{ + int val; + val = ioport_read_table[1][addr](ioport_opaque[addr], addr); +#ifdef DEBUG_IOPORT + if (loglevel & CPU_LOG_IOPORT) + fprintf(logfile, "inw : %04x %04x\n", addr, val); +#endif +#ifdef USE_KQEMU + if (env) + env->last_io_time = cpu_get_time_fast(); +#endif + return val; +} + +int cpu_inl(CPUState *env, int addr) +{ + int val; + val = ioport_read_table[2][addr](ioport_opaque[addr], addr); +#ifdef DEBUG_IOPORT + if (loglevel & CPU_LOG_IOPORT) + fprintf(logfile, "inl : %04x %08x\n", addr, val); +#endif +#ifdef USE_KQEMU + if (env) + env->last_io_time = cpu_get_time_fast(); +#endif + return val; +} + +/***********************************************************/ +void hw_error(const char *fmt, ...) +{ + va_list ap; +#ifndef CONFIG_DM + CPUState *env; +#endif /* !CONFIG_DM */ + + va_start(ap, fmt); + fprintf(stderr, "qemu: hardware error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); +#ifndef CONFIG_DM + for(env = first_cpu; env != NULL; env = env->next_cpu) { + fprintf(stderr, "CPU #%d:\n", env->cpu_index); +#ifdef TARGET_I386 + cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU); +#else + cpu_dump_state(env, stderr, fprintf, 0); +#endif + } +#endif /* !CONFIG_DM */ + va_end(ap); + abort(); +} + +/***********************************************************/ +/* keyboard/mouse */ + +static QEMUPutKBDEvent *qemu_put_kbd_event; +static void *qemu_put_kbd_event_opaque; +static QEMUPutMouseEvent *qemu_put_mouse_event; +static void *qemu_put_mouse_event_opaque; +static int qemu_put_mouse_event_absolute; + +void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) +{ + qemu_put_kbd_event_opaque = opaque; + qemu_put_kbd_event = func; +} + +void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute) +{ + qemu_put_mouse_event_opaque = opaque; + qemu_put_mouse_event = func; + qemu_put_mouse_event_absolute = absolute; +} + +void kbd_put_keycode(int keycode) +{ + if (qemu_put_kbd_event) { + qemu_put_kbd_event(qemu_put_kbd_event_opaque, keycode); + } +} + +void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) +{ + if (qemu_put_mouse_event) { + qemu_put_mouse_event(qemu_put_mouse_event_opaque, + dx, dy, dz, buttons_state); + } +} + +int kbd_mouse_is_absolute(void) +{ + return qemu_put_mouse_event_absolute; +} + +/***********************************************************/ +/* timers */ + +#if defined(__powerpc__) + +static inline uint32_t get_tbl(void) +{ + uint32_t tbl; + asm volatile("mftb %0" : "=r" (tbl)); + return tbl; +} + +static inline uint32_t get_tbu(void) +{ + uint32_t tbl; + asm volatile("mftbu %0" : "=r" (tbl)); + return tbl; +} + +int64_t cpu_get_real_ticks(void) +{ + uint32_t l, h, h1; + /* NOTE: we test if wrapping has occurred */ + do { + h = get_tbu(); + l = get_tbl(); + h1 = get_tbu(); + } while (h != h1); + return ((int64_t)h << 32) | l; +} + +#elif defined(__i386__) + +int64_t cpu_get_real_ticks(void) +{ +#ifdef _WIN32 + LARGE_INTEGER ti; + QueryPerformanceCounter(&ti); + return ti.QuadPart; +#else + int64_t val; + asm volatile ("rdtsc" : "=A" (val)); + return val; +#endif +} + +#elif defined(__x86_64__) + +int64_t cpu_get_real_ticks(void) +{ + uint32_t low,high; + int64_t val; + asm volatile("rdtsc" : "=a" (low), "=d" (high)); + val = high; + val <<= 32; + val |= low; + return val; +} + +#elif defined(__ia64) + +int64_t cpu_get_real_ticks(void) +{ + int64_t val; + asm volatile ("mov %0 = ar.itc" : "=r"(val) :: "memory"); + return val; +} + +#elif defined(__s390__) + +int64_t cpu_get_real_ticks(void) +{ + int64_t val; + asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc"); + return val; +} + +#else +#error unsupported CPU +#endif + +static int64_t cpu_ticks_prev; +static int64_t cpu_ticks_offset; +static int cpu_ticks_enabled; + +static inline int64_t cpu_get_ticks(void) +{ + if (!cpu_ticks_enabled) { + return cpu_ticks_offset; + } else { + int64_t ticks; + ticks = cpu_get_real_ticks(); + if (cpu_ticks_prev > ticks) { + /* Note: non increasing ticks may happen if the host uses + software suspend */ + cpu_ticks_offset += cpu_ticks_prev - ticks; + } + cpu_ticks_prev = ticks; + return ticks + cpu_ticks_offset; + } +} + +/* enable cpu_get_ticks() */ +void cpu_enable_ticks(void) +{ + if (!cpu_ticks_enabled) { + cpu_ticks_offset -= cpu_get_real_ticks(); + cpu_ticks_enabled = 1; + } +} + +/* disable cpu_get_ticks() : the clock is stopped. You must not call + cpu_get_ticks() after that. */ +void cpu_disable_ticks(void) +{ + if (cpu_ticks_enabled) { + cpu_ticks_offset = cpu_get_ticks(); + cpu_ticks_enabled = 0; + } +} + +#ifdef _WIN32 +void cpu_calibrate_ticks(void) +{ + LARGE_INTEGER freq; + int ret; + + ret = QueryPerformanceFrequency(&freq); + if (ret == 0) { + fprintf(stderr, "Could not calibrate ticks\n"); + exit(1); + } + ticks_per_sec = freq.QuadPart; +} + +#else +static int64_t get_clock(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000LL + tv.tv_usec; +} + +void cpu_calibrate_ticks(void) +{ + int64_t usec, ticks; + + usec = get_clock(); + ticks = cpu_get_real_ticks(); + usleep(50 * 1000); + usec = get_clock() - usec; + ticks = cpu_get_real_ticks() - ticks; + ticks_per_sec = (ticks * 1000000LL + (usec >> 1)) / usec; +} +#endif /* !_WIN32 */ + +/* compute with 96 bit intermediate result: (a*b)/c */ +uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) +{ + union { + uint64_t ll; + struct { +#ifdef WORDS_BIGENDIAN + uint32_t high, low; +#else + uint32_t low, high; +#endif + } l; + } u, res; + uint64_t rl, rh; + + u.ll = a; + rl = (uint64_t)u.l.low * (uint64_t)b; + rh = (uint64_t)u.l.high * (uint64_t)b; + rh += (rl >> 32); + res.l.high = rh / c; + res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; + return res.ll; +} + +#define QEMU_TIMER_REALTIME 0 +#define QEMU_TIMER_VIRTUAL 1 + +struct QEMUClock { + int type; + /* XXX: add frequency */ +}; + +struct QEMUTimer { + QEMUClock *clock; + int64_t expire_time; + QEMUTimerCB *cb; + void *opaque; + struct QEMUTimer *next; +}; + +QEMUClock *rt_clock; +QEMUClock *vm_clock; + +static QEMUTimer *active_timers[2]; +#ifdef _WIN32 +static MMRESULT timerID; +static HANDLE host_alarm = NULL; +static unsigned int period = 1; +#else +/* frequency of the times() clock tick */ +static int timer_freq; +#endif + +QEMUClock *qemu_new_clock(int type) +{ + QEMUClock *clock; + clock = qemu_mallocz(sizeof(QEMUClock)); + if (!clock) + return NULL; + clock->type = type; + return clock; +} + +QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque) +{ + QEMUTimer *ts; + + ts = qemu_mallocz(sizeof(QEMUTimer)); + ts->clock = clock; + ts->cb = cb; + ts->opaque = opaque; + return ts; +} + +void qemu_free_timer(QEMUTimer *ts) +{ + qemu_free(ts); +} + +/* stop a timer, but do not dealloc it */ +void qemu_del_timer(QEMUTimer *ts) +{ + QEMUTimer **pt, *t; + + /* NOTE: this code must be signal safe because + qemu_timer_expired() can be called from a signal. */ + pt = &active_timers[ts->clock->type]; + for(;;) { + t = *pt; + if (!t) + break; + if (t == ts) { + *pt = t->next; + break; + } + pt = &t->next; + } +} + +/* modify the current timer so that it will be fired when current_time + >= expire_time. The corresponding callback will be called. */ +void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) +{ + QEMUTimer **pt, *t; + + qemu_del_timer(ts); + + /* add the timer in the sorted list */ + /* NOTE: this code must be signal safe because + qemu_timer_expired() can be called from a signal. */ + pt = &active_timers[ts->clock->type]; + for(;;) { + t = *pt; + if (!t) + break; + if (t->expire_time > expire_time) + break; + pt = &t->next; + } + ts->expire_time = expire_time; + ts->next = *pt; + *pt = ts; +} + +int qemu_timer_pending(QEMUTimer *ts) +{ + QEMUTimer *t; + for(t = active_timers[ts->clock->type]; t != NULL; t = t->next) { + if (t == ts) + return 1; + } + return 0; +} + +static inline int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time) +{ + if (!timer_head) + return 0; + return (timer_head->expire_time <= current_time); +} + +static void qemu_run_timers(QEMUTimer **ptimer_head, int64_t current_time) +{ + QEMUTimer *ts; + + for(;;) { + ts = *ptimer_head; + if (!ts || ts->expire_time > current_time) + break; + /* remove timer from the list before calling the callback */ + *ptimer_head = ts->next; + ts->next = NULL; + + /* run the callback (the timer list can be modified) */ + ts->cb(ts->opaque); + } +} + +int64_t qemu_get_clock(QEMUClock *clock) +{ + switch(clock->type) { + case QEMU_TIMER_REALTIME: +#ifdef _WIN32 + return GetTickCount(); +#else + { + struct tms tp; + + /* Note that using gettimeofday() is not a good solution + for timers because its value change when the date is + modified. */ + if (timer_freq == 100) { + return times(&tp) * 10; + } else { + return ((int64_t)times(&tp) * 1000) / timer_freq; + } + } +#endif + default: + case QEMU_TIMER_VIRTUAL: + return cpu_get_ticks(); + } +} + +/* save a timer */ +void qemu_put_timer(QEMUFile *f, QEMUTimer *ts) +{ + uint64_t expire_time; + + if (qemu_timer_pending(ts)) { + expire_time = ts->expire_time; + } else { + expire_time = -1; + } + qemu_put_be64(f, expire_time); +} + +void qemu_get_timer(QEMUFile *f, QEMUTimer *ts) +{ + uint64_t expire_time; + + expire_time = qemu_get_be64(f); + if (expire_time != -1) { + qemu_mod_timer(ts, expire_time); + } else { + qemu_del_timer(ts); + } +} + +#ifdef CONFIG_DM +static void timer_save(QEMUFile *f, void *opaque) +{ +} + +static int timer_load(QEMUFile *f, void *opaque, int version_id) +{ + return 0; +} +#else /* !CONFIG_DM */ +static void timer_save(QEMUFile *f, void *opaque) +{ + if (cpu_ticks_enabled) { + hw_error("cannot save state if virtual timers are running"); + } + qemu_put_be64s(f, &cpu_ticks_offset); + qemu_put_be64s(f, &ticks_per_sec); +} + +static int timer_load(QEMUFile *f, void *opaque, int version_id) +{ + if (version_id != 1) + return -EINVAL; + if (cpu_ticks_enabled) { + return -EINVAL; + } + qemu_get_be64s(f, &cpu_ticks_offset); + qemu_get_be64s(f, &ticks_per_sec); + return 0; +} + +#ifdef _WIN32 +void CALLBACK host_alarm_handler(UINT uTimerID, UINT uMsg, + DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) +#else +static void host_alarm_handler(int host_signum) +#endif +{ +#if 0 +#define DISP_FREQ 1000 + { + static int64_t delta_min = INT64_MAX; + static int64_t delta_max, delta_cum, last_clock, delta, ti; + static int count; + ti = qemu_get_clock(vm_clock); + if (last_clock != 0) { + delta = ti - last_clock; + if (delta < delta_min) + delta_min = delta; + if (delta > delta_max) + delta_max = delta; + delta_cum += delta; + if (++count == DISP_FREQ) { + printf("timer: min=%lld us max=%lld us avg=%lld us avg_freq=%0.3f Hz\n", + muldiv64(delta_min, 1000000, ticks_per_sec), + muldiv64(delta_max, 1000000, ticks_per_sec), + muldiv64(delta_cum, 1000000 / DISP_FREQ, ticks_per_sec), + (double)ticks_per_sec / ((double)delta_cum / DISP_FREQ)); + count = 0; + delta_min = INT64_MAX; + delta_max = 0; + delta_cum = 0; + } + } + last_clock = ti; + } +#endif + if (qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL], + qemu_get_clock(vm_clock)) || + qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME], + qemu_get_clock(rt_clock))) { +#ifdef _WIN32 + SetEvent(host_alarm); +#endif + CPUState *env = cpu_single_env; + if (env) { + /* stop the currently executing cpu because a timer occured */ + cpu_interrupt(env, CPU_INTERRUPT_EXIT); +#ifdef USE_KQEMU + if (env->kqemu_enabled) { + kqemu_cpu_interrupt(env); + } +#endif + } + } +} + +#ifndef _WIN32 + +#if defined(__linux__) + +#define RTC_FREQ 1024 + +static int rtc_fd; + +static int start_rtc_timer(void) +{ + rtc_fd = open("/dev/rtc", O_RDONLY); + if (rtc_fd < 0) + return -1; + if (ioctl(rtc_fd, RTC_IRQP_SET, RTC_FREQ) < 0) { + fprintf(stderr, "Could not configure '/dev/rtc' to have a 1024 Hz timer. This is not a fatal\n" + "error, but for better emulation accuracy either use a 2.6 host Linux kernel or\n" + "type 'echo 1024 > /proc/sys/dev/rtc/max-user-freq' as root.\n"); + goto fail; + } + if (ioctl(rtc_fd, RTC_PIE_ON, 0) < 0) { + fail: + close(rtc_fd); + return -1; + } + pit_min_timer_count = PIT_FREQ / RTC_FREQ; + return 0; +} + +#else + +static int start_rtc_timer(void) +{ + return -1; +} + +#endif /* !defined(__linux__) */ + +#endif /* !defined(_WIN32) */ + +#endif /* !CONFIG_DM */ + +static void init_timers(void) +{ + rt_clock = qemu_new_clock(QEMU_TIMER_REALTIME); + vm_clock = qemu_new_clock(QEMU_TIMER_VIRTUAL); + +#ifdef _WIN32 + { + int count=0; + TIMECAPS tc; + + ZeroMemory(&tc, sizeof(TIMECAPS)); + timeGetDevCaps(&tc, sizeof(TIMECAPS)); + if (period < tc.wPeriodMin) + period = tc.wPeriodMin; + timeBeginPeriod(period); + timerID = timeSetEvent(1, // interval (ms) + period, // resolution + host_alarm_handler, // function + (DWORD)&count, // user parameter + TIME_PERIODIC | TIME_CALLBACK_FUNCTION); + if( !timerID ) { + perror("failed timer alarm"); + exit(1); + } + host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!host_alarm) { + perror("failed CreateEvent"); + exit(1); + } + ResetEvent(host_alarm); + } + pit_min_timer_count = ((uint64_t)10000 * PIT_FREQ) / 1000000; +#else + { +#ifndef CONFIG_DM + struct sigaction act; + struct itimerval itv; +#endif + + /* get times() syscall frequency */ + timer_freq = sysconf(_SC_CLK_TCK); + +#ifndef CONFIG_DM + /* timer signal */ + sigfillset(&act.sa_mask); + act.sa_flags = 0; +#if defined (TARGET_I386) && defined(USE_CODE_COPY) + act.sa_flags |= SA_ONSTACK; +#endif + act.sa_handler = host_alarm_handler; + sigaction(SIGALRM, &act, NULL); + + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 999; /* for i386 kernel 2.6 to get 1 ms */ + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = 10 * 1000; + setitimer(ITIMER_REAL, &itv, NULL); + /* we probe the tick duration of the kernel to inform the user if + the emulated kernel requested a too high timer frequency */ + getitimer(ITIMER_REAL, &itv); + +#if defined(__linux__) + /* XXX: force /dev/rtc usage because even 2.6 kernels may not + have timers with 1 ms resolution. The correct solution will + be to use the POSIX real time timers available in recent + 2.6 kernels */ + if (itv.it_interval.tv_usec > 1000 || 1) { + /* try to use /dev/rtc to have a faster timer */ + if (start_rtc_timer() < 0) + goto use_itimer; + /* disable itimer */ + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = 0; + setitimer(ITIMER_REAL, &itv, NULL); + + /* use the RTC */ + sigaction(SIGIO, &act, NULL); + fcntl(rtc_fd, F_SETFL, O_ASYNC); + fcntl(rtc_fd, F_SETOWN, getpid()); + } else +#endif /* defined(__linux__) */ + { + use_itimer: + pit_min_timer_count = ((uint64_t)itv.it_interval.tv_usec * + PIT_FREQ) / 1000000; + } +#endif /* CONFIG_DM */ + } +#endif +} + +void quit_timers(void) +{ +#ifdef _WIN32 + timeKillEvent(timerID); + timeEndPeriod(period); + if (host_alarm) { + CloseHandle(host_alarm); + host_alarm = NULL; + } +#endif +} + +/***********************************************************/ +/* character device */ + +int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len) +{ + return s->chr_write(s, buf, len); +} + +int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg) +{ + if (!s->chr_ioctl) + return -ENOTSUP; + return s->chr_ioctl(s, cmd, arg); +} + +void qemu_chr_printf(CharDriverState *s, const char *fmt, ...) +{ + char buf[4096]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + qemu_chr_write(s, buf, strlen(buf)); + va_end(ap); +} + +void qemu_chr_send_event(CharDriverState *s, int event) +{ + if (s->chr_send_event) + s->chr_send_event(s, event); +} + +void qemu_chr_add_read_handler(CharDriverState *s, + IOCanRWHandler *fd_can_read, + IOReadHandler *fd_read, void *opaque) +{ + s->chr_add_read_handler(s, fd_can_read, fd_read, opaque); +} + +void qemu_chr_add_event_handler(CharDriverState *s, IOEventHandler *chr_event) +{ + s->chr_event = chr_event; +} + +static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + return len; +} + +static void null_chr_add_read_handler(CharDriverState *chr, + IOCanRWHandler *fd_can_read, + IOReadHandler *fd_read, void *opaque) +{ +} + +CharDriverState *qemu_chr_open_null(void) +{ + CharDriverState *chr; + + chr = qemu_mallocz(sizeof(CharDriverState)); + if (!chr) + return NULL; + chr->chr_write = null_chr_write; + chr->chr_add_read_handler = null_chr_add_read_handler; + return chr; +} + +#ifdef _WIN32 + +static void socket_cleanup(void) +{ + WSACleanup(); +} + +static int socket_init(void) +{ + WSADATA Data; + int ret, err; + + ret = WSAStartup(MAKEWORD(2,2), &Data); + if (ret != 0) { + err = WSAGetLastError(); + fprintf(stderr, "WSAStartup: %d\n", err); + return -1; + } + atexit(socket_cleanup); + return 0; +} + +static int send_all(int fd, const uint8_t *buf, int len1) +{ + int ret, len; + + len = len1; + while (len > 0) { + ret = send(fd, buf, len, 0); + if (ret < 0) { + int errno; + errno = WSAGetLastError(); + if (errno != WSAEWOULDBLOCK) { + return -1; + } + } else if (ret == 0) { + break; + } else { + buf += ret; + len -= ret; + } + } + return len1 - len; +} + +void socket_set_nonblock(int fd) +{ + unsigned long opt = 1; + ioctlsocket(fd, FIONBIO, &opt); +} + +#else + +static int unix_write(int fd, const uint8_t *buf, int len1) +{ + int ret, sel_ret, len; + int max_fd; + fd_set writefds; + struct timeval timeout; + + max_fd = fd; + + len = len1; + while (len > 0) { + FD_ZERO(&writefds); + FD_SET(fd, &writefds); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + sel_ret = select(max_fd + 1, NULL, &writefds, 0, &timeout); + if (sel_ret <= 0) { + /* Timeout or select error */ + return -1; + } else { + ret = write(fd, buf, len); + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) + return -1; + } else if (ret == 0) { + break; + } else { + buf += ret; + len -= ret; + } + } + } + return len1 - len; +} + +static inline int send_all(int fd, const uint8_t *buf, int len1) +{ + return unix_write(fd, buf, len1); +} + +void socket_set_nonblock(int fd) +{ + fcntl(fd, F_SETFL, O_NONBLOCK); +} +#endif /* !_WIN32 */ + +#ifndef _WIN32 + +typedef struct { + int fd_in, fd_out; + IOCanRWHandler *fd_can_read; + IOReadHandler *fd_read; + void *fd_opaque; + int max_size; +} FDCharDriver; + +#define STDIO_MAX_CLIENTS 2 + +static int stdio_nb_clients; +static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS]; + +static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + FDCharDriver *s = chr->opaque; + return unix_write(s->fd_out, buf, len); +} + +static int fd_chr_read_poll(void *opaque) +{ + CharDriverState *chr = opaque; + FDCharDriver *s = chr->opaque; + + s->max_size = s->fd_can_read(s->fd_opaque); + return s->max_size; +} + +static void fd_chr_read(void *opaque) +{ + CharDriverState *chr = opaque; + FDCharDriver *s = chr->opaque; + int size, len; + uint8_t buf[1024]; + + len = sizeof(buf); + if (len > s->max_size) + len = s->max_size; + if (len == 0) + return; + size = read(s->fd_in, buf, len); + if (size > 0) { + s->fd_read(s->fd_opaque, buf, size); + } +} + +static void fd_chr_add_read_handler(CharDriverState *chr, + IOCanRWHandler *fd_can_read, + IOReadHandler *fd_read, void *opaque) +{ + FDCharDriver *s = chr->opaque; + + if (s->fd_in >= 0) { + s->fd_can_read = fd_can_read; + s->fd_read = fd_read; + s->fd_opaque = opaque; + if (nographic && s->fd_in == 0) { + } else { + qemu_set_fd_handler2(s->fd_in, fd_chr_read_poll, + fd_chr_read, NULL, chr); + } + } +} + +/* open a character device to a unix fd */ +CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) +{ + CharDriverState *chr; + FDCharDriver *s; + + chr = qemu_mallocz(sizeof(CharDriverState)); + if (!chr) + return NULL; + s = qemu_mallocz(sizeof(FDCharDriver)); + if (!s) { + free(chr); + return NULL; + } + s->fd_in = fd_in; + s->fd_out = fd_out; + chr->opaque = s; + chr->chr_write = fd_chr_write; + chr->chr_add_read_handler = fd_chr_add_read_handler; + return chr; +} + +CharDriverState *qemu_chr_open_file_out(const char *file_out) +{ + int fd_out; + + fd_out = open(file_out, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666); + if (fd_out < 0) + return NULL; + return qemu_chr_open_fd(-1, fd_out); +} + +CharDriverState *qemu_chr_open_pipe(const char *filename) +{ + int fd; + + fd = open(filename, O_RDWR | O_BINARY); + if (fd < 0) + return NULL; + return qemu_chr_open_fd(fd, fd); +} + + +/* for STDIO, we handle the case where several clients use it + (nographic mode) */ + +#define TERM_ESCAPE 0x01 /* ctrl-a is used for escape */ + +#define TERM_FIFO_MAX_SIZE 1 + +static int term_got_escape, client_index; +static uint8_t term_fifo[TERM_FIFO_MAX_SIZE]; +int term_fifo_size; + +void term_print_help(void) +{ + printf("\n" + "C-a h print this help\n" + "C-a x exit emulator\n" + "C-a s save disk data back to file (if -snapshot)\n" + "C-a b send break (magic sysrq)\n" + "C-a c switch between console and monitor\n" + "C-a C-a send C-a\n" + ); +} + +/* called when a char is received */ +static void stdio_received_byte(int ch) +{ + if (term_got_escape) { + term_got_escape = 0; + switch(ch) { + case 'h': + term_print_help(); + break; + case 'x': + exit(0); + break; + case 's': + { + int i; + for (i = 0; i < MAX_DISKS; i++) { + if (bs_table[i]) + bdrv_commit(bs_table[i]); + } + } + break; + case 'b': + if (client_index < stdio_nb_clients) { + CharDriverState *chr; + FDCharDriver *s; + + chr = stdio_clients[client_index]; + s = chr->opaque; + chr->chr_event(s->fd_opaque, CHR_EVENT_BREAK); + } + break; + case 'c': + client_index++; + if (client_index >= stdio_nb_clients) + client_index = 0; + if (client_index == 0) { + /* send a new line in the monitor to get the prompt */ + ch = '\r'; + goto send_char; + } + break; + case TERM_ESCAPE: + goto send_char; + } + } else if (ch == TERM_ESCAPE) { + term_got_escape = 1; + } else { + send_char: + if (client_index < stdio_nb_clients) { + uint8_t buf[1]; + CharDriverState *chr; + FDCharDriver *s; + + chr = stdio_clients[client_index]; + s = chr->opaque; + if (s->fd_can_read(s->fd_opaque) > 0) { + buf[0] = ch; + s->fd_read(s->fd_opaque, buf, 1); + } else if (term_fifo_size == 0) { + term_fifo[term_fifo_size++] = ch; + } + } + } +} + +static int stdio_read_poll(void *opaque) +{ + CharDriverState *chr; + FDCharDriver *s; + + if (client_index < stdio_nb_clients) { + chr = stdio_clients[client_index]; + s = chr->opaque; + /* try to flush the queue if needed */ + if (term_fifo_size != 0 && s->fd_can_read(s->fd_opaque) > 0) { + s->fd_read(s->fd_opaque, term_fifo, 1); + term_fifo_size = 0; + } + /* see if we can absorb more chars */ + if (term_fifo_size == 0) + return 1; + else + return 0; + } else { + return 1; + } +} + +static void stdio_read(void *opaque) +{ + int size; + uint8_t buf[1]; + + size = read(0, buf, 1); + if (size > 0) + stdio_received_byte(buf[0]); +} + +/* init terminal so that we can grab keys */ +static struct termios oldtty; +static int old_fd0_flags; + +static void term_exit(void) +{ + tcsetattr (0, TCSANOW, &oldtty); + fcntl(0, F_SETFL, old_fd0_flags); +} + +static void term_init(void) +{ + struct termios tty; + + tcgetattr (0, &tty); + oldtty = tty; + old_fd0_flags = fcntl(0, F_GETFL); + + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + tty.c_oflag |= OPOST; + tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); + /* if graphical mode, we allow Ctrl-C handling */ + if (nographic) + tty.c_lflag &= ~ISIG; + tty.c_cflag &= ~(CSIZE|PARENB); + tty.c_cflag |= CS8; + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + + tcsetattr (0, TCSANOW, &tty); + + atexit(term_exit); + + fcntl(0, F_SETFL, O_NONBLOCK); +} + +CharDriverState *qemu_chr_open_stdio(void) +{ + CharDriverState *chr; + + if (nographic) { + if (stdio_nb_clients >= STDIO_MAX_CLIENTS) + return NULL; + chr = qemu_chr_open_fd(0, 1); + if (stdio_nb_clients == 0) + qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, NULL); + client_index = stdio_nb_clients; + } else { + if (stdio_nb_clients != 0) + return NULL; + chr = qemu_chr_open_fd(0, 1); + } + stdio_clients[stdio_nb_clients++] = chr; + if (stdio_nb_clients == 1) { + /* set the terminal in raw mode */ + term_init(); + } + return chr; +} + +int store_console_dev(int domid, char *pts) +{ + int xc_handle; + struct xs_handle *xs; + char *path; + + xs = xs_daemon_open(); + if (xs == NULL) { + fprintf(logfile, "Could not contact XenStore\n"); + return -1; + } + + xc_handle = xc_interface_open(); + if (xc_handle == -1) { + fprintf(logfile, "xc_interface_open() error\n"); + return -1; + } + + path = xs_get_domain_path(xs, domid); + if (path == NULL) { + fprintf(logfile, "xs_get_domain_path() error\n"); + return -1; + } + path = realloc(path, strlen(path) + strlen("/console/tty") + 1); + if (path == NULL) { + fprintf(logfile, "realloc error\n"); + return -1; + } + strcat(path, "/console/tty"); + if (!xs_write(xs, XBT_NULL, path, pts, strlen(pts))) { + fprintf(logfile, "xs_write for console fail"); + return -1; + } + + free(path); + xs_daemon_close(xs); + close(xc_handle); + + return 0; +} + +#if defined(__linux__) +CharDriverState *qemu_chr_open_pty(void) +{ + struct termios tty; + int master_fd, slave_fd; + + /* Not satisfying */ + if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) < 0) { + return NULL; + } + + /* Set raw attributes on the pty. */ + cfmakeraw(&tty); + tcsetattr(slave_fd, TCSAFLUSH, &tty); + + fprintf(stderr, "char device redirected to %s\n", ptsname(master_fd)); + store_console_dev(domid, ptsname(master_fd)); + + return qemu_chr_open_fd(master_fd, master_fd); +} + +static void tty_serial_init(int fd, int speed, + int parity, int data_bits, int stop_bits) +{ + struct termios tty; + speed_t spd; + +#if 0 + printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n", + speed, parity, data_bits, stop_bits); +#endif + tcgetattr (fd, &tty); + + switch(speed) { + case 50: + spd = B50; + break; + case 75: + spd = B75; + break; + case 300: + spd = B300; + break; + case 600: + spd = B600; + break; + case 1200: + spd = B1200; + break; + case 2400: + spd = B2400; + break; + case 4800: + spd = B4800; + break; + case 9600: + spd = B9600; + break; + case 19200: + spd = B19200; + break; + case 38400: + spd = B38400; + break; + case 57600: + spd = B57600; + break; + default: + case 115200: + spd = B115200; + break; + } + + cfsetispeed(&tty, spd); + cfsetospeed(&tty, spd); + + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + tty.c_oflag |= OPOST; + tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG); + tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS); + switch(data_bits) { + default: + case 8: + tty.c_cflag |= CS8; + break; + case 7: + tty.c_cflag |= CS7; + break; + case 6: + tty.c_cflag |= CS6; + break; + case 5: + tty.c_cflag |= CS5; + break; + } + switch(parity) { + default: + case 'N': + break; + case 'E': + tty.c_cflag |= PARENB; + break; + case 'O': + tty.c_cflag |= PARENB | PARODD; + break; + } + + tcsetattr (fd, TCSANOW, &tty); +} + +static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) +{ + FDCharDriver *s = chr->opaque; + + switch(cmd) { + case CHR_IOCTL_SERIAL_SET_PARAMS: + { + QEMUSerialSetParams *ssp = arg; + tty_serial_init(s->fd_in, ssp->speed, ssp->parity, + ssp->data_bits, ssp->stop_bits); + } + break; + case CHR_IOCTL_SERIAL_SET_BREAK: + { + int enable = *(int *)arg; + if (enable) + tcsendbreak(s->fd_in, 1); + } + break; + default: + return -ENOTSUP; + } + return 0; +} + +CharDriverState *qemu_chr_open_tty(const char *filename) +{ + CharDriverState *chr; + int fd; + + fd = open(filename, O_RDWR | O_NONBLOCK); + if (fd < 0) + return NULL; + fcntl(fd, F_SETFL, O_NONBLOCK); + tty_serial_init(fd, 115200, 'N', 8, 1); + chr = qemu_chr_open_fd(fd, fd); + if (!chr) + return NULL; + chr->chr_ioctl = tty_serial_ioctl; + return chr; +} + +static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) +{ + int fd = (int)chr->opaque; + uint8_t b; + + switch(cmd) { + case CHR_IOCTL_PP_READ_DATA: + if (ioctl(fd, PPRDATA, &b) < 0) + return -ENOTSUP; + *(uint8_t *)arg = b; + break; + case CHR_IOCTL_PP_WRITE_DATA: + b = *(uint8_t *)arg; + if (ioctl(fd, PPWDATA, &b) < 0) + return -ENOTSUP; + break; + case CHR_IOCTL_PP_READ_CONTROL: + if (ioctl(fd, PPRCONTROL, &b) < 0) + return -ENOTSUP; + *(uint8_t *)arg = b; + break; + case CHR_IOCTL_PP_WRITE_CONTROL: + b = *(uint8_t *)arg; + if (ioctl(fd, PPWCONTROL, &b) < 0) + return -ENOTSUP; + break; + case CHR_IOCTL_PP_READ_STATUS: + if (ioctl(fd, PPRSTATUS, &b) < 0) + return -ENOTSUP; + *(uint8_t *)arg = b; + break; + default: + return -ENOTSUP; + } + return 0; +} + +CharDriverState *qemu_chr_open_pp(const char *filename) +{ + CharDriverState *chr; + int fd; + + fd = open(filename, O_RDWR); + if (fd < 0) + return NULL; + + if (ioctl(fd, PPCLAIM) < 0) { + close(fd); + return NULL; + } + + chr = qemu_mallocz(sizeof(CharDriverState)); + if (!chr) { + close(fd); + return NULL; + } + chr->opaque = (void *)fd; + chr->chr_write = null_chr_write; + chr->chr_add_read_handler = null_chr_add_read_handler; + chr->chr_ioctl = pp_ioctl; + return chr; +} + +#else +CharDriverState *qemu_chr_open_pty(void) +{ + return NULL; +} +#endif + +#endif /* !defined(_WIN32) */ + +#ifdef _WIN32 +typedef struct { + IOCanRWHandler *fd_can_read; + IOReadHandler *fd_read; + void *win_opaque; + int max_size; + HANDLE hcom, hrecv, hsend; + OVERLAPPED orecv, osend; + BOOL fpipe; + DWORD len; +} WinCharState; + +#define NSENDBUF 2048 +#define NRECVBUF 2048 +#define MAXCONNECT 1 +#define NTIMEOUT 5000 + +static int win_chr_poll(void *opaque); +static int win_chr_pipe_poll(void *opaque); + +static void win_chr_close2(WinCharState *s) +{ + if (s->hsend) { + CloseHandle(s->hsend); + s->hsend = NULL; + } + if (s->hrecv) { + CloseHandle(s->hrecv); + s->hrecv = NULL; + } + if (s->hcom) { + CloseHandle(s->hcom); + s->hcom = NULL; + } + if (s->fpipe) + qemu_del_polling_cb(win_chr_pipe_poll, s); + else + qemu_del_polling_cb(win_chr_poll, s); +} + +static void win_chr_close(CharDriverState *chr) +{ + WinCharState *s = chr->opaque; + win_chr_close2(s); +} + +static int win_chr_init(WinCharState *s, const char *filename) +{ + COMMCONFIG comcfg; + COMMTIMEOUTS cto = { 0, 0, 0, 0, 0}; + COMSTAT comstat; + DWORD size; + DWORD err; + + s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!s->hsend) { + fprintf(stderr, "Failed CreateEvent\n"); + goto fail; + } + s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!s->hrecv) { + fprintf(stderr, "Failed CreateEvent\n"); + goto fail; + } + + s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + if (s->hcom == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Failed CreateFile (%lu)\n", GetLastError()); + s->hcom = NULL; + goto fail; + } + + if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) { + fprintf(stderr, "Failed SetupComm\n"); + goto fail; + } + + ZeroMemory(&comcfg, sizeof(COMMCONFIG)); + size = sizeof(COMMCONFIG); + GetDefaultCommConfig(filename, &comcfg, &size); + comcfg.dcb.DCBlength = sizeof(DCB); + CommConfigDialog(filename, NULL, &comcfg); + + if (!SetCommState(s->hcom, &comcfg.dcb)) { + fprintf(stderr, "Failed SetCommState\n"); + goto fail; + } + + if (!SetCommMask(s->hcom, EV_ERR)) { + fprintf(stderr, "Failed SetCommMask\n"); + goto fail; + } + + cto.ReadIntervalTimeout = MAXDWORD; + if (!SetCommTimeouts(s->hcom, &cto)) { + fprintf(stderr, "Failed SetCommTimeouts\n"); + goto fail; + } + + if (!ClearCommError(s->hcom, &err, &comstat)) { + fprintf(stderr, "Failed ClearCommError\n"); + goto fail; + } + qemu_add_polling_cb(win_chr_poll, s); + return 0; + + fail: + win_chr_close2(s); + return -1; +} + +static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1) +{ + WinCharState *s = chr->opaque; + DWORD len, ret, size, err; + + len = len1; + ZeroMemory(&s->osend, sizeof(s->osend)); + s->osend.hEvent = s->hsend; + while (len > 0) { + if (s->hsend) + ret = WriteFile(s->hcom, buf, len, &size, &s->osend); + else + ret = WriteFile(s->hcom, buf, len, &size, NULL); + if (!ret) { + err = GetLastError(); + if (err == ERROR_IO_PENDING) { + ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE); + if (ret) { + buf += size; + len -= size; + } else { + break; + } + } else { + break; + } + } else { + buf += size; + len -= size; + } + } + return len1 - len; +} + +static int win_chr_read_poll(WinCharState *s) +{ + s->max_size = s->fd_can_read(s->win_opaque); + return s->max_size; +} + +static void win_chr_readfile(WinCharState *s) +{ + int ret, err; + uint8_t buf[1024]; + DWORD size; + + ZeroMemory(&s->orecv, sizeof(s->orecv)); + s->orecv.hEvent = s->hrecv; + ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv); + if (!ret) { + err = GetLastError(); + if (err == ERROR_IO_PENDING) { + ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE); + } + } + + if (size > 0) { + s->fd_read(s->win_opaque, buf, size); + } +} + +static void win_chr_read(WinCharState *s) +{ + if (s->len > s->max_size) + s->len = s->max_size; + if (s->len == 0) + return; + + win_chr_readfile(s); +} + +static int win_chr_poll(void *opaque) +{ + WinCharState *s = opaque; + COMSTAT status; + DWORD comerr; + + ClearCommError(s->hcom, &comerr, &status); + if (status.cbInQue > 0) { + s->len = status.cbInQue; + win_chr_read_poll(s); + win_chr_read(s); + return 1; + } + return 0; +} + +static void win_chr_add_read_handler(CharDriverState *chr, + IOCanRWHandler *fd_can_read, + IOReadHandler *fd_read, void *opaque) +{ + WinCharState *s = chr->opaque; + + s->fd_can_read = fd_can_read; + s->fd_read = fd_read; + s->win_opaque = opaque; +} + +CharDriverState *qemu_chr_open_win(const char *filename) +{ + CharDriverState *chr; + WinCharState *s; + + chr = qemu_mallocz(sizeof(CharDriverState)); + if (!chr) + return NULL; + s = qemu_mallocz(sizeof(WinCharState)); + if (!s) { + free(chr); + return NULL; + } + chr->opaque = s; + chr->chr_write = win_chr_write; + chr->chr_add_read_handler = win_chr_add_read_handler; + chr->chr_close = win_chr_close; + + if (win_chr_init(s, filename) < 0) { + free(s); + free(chr); + return NULL; + } + return chr; +} + +static int win_chr_pipe_poll(void *opaque) +{ + WinCharState *s = opaque; + DWORD size; + + PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL); + if (size > 0) { + s->len = size; + win_chr_read_poll(s); + win_chr_read(s); + return 1; + } + return 0; +} + +static int win_chr_pipe_init(WinCharState *s, const char *filename) +{ + OVERLAPPED ov; + int ret; + DWORD size; + char openname[256]; + + s->fpipe = TRUE; + + s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!s->hsend) { + fprintf(stderr, "Failed CreateEvent\n"); + goto fail; + } + s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!s->hrecv) { + fprintf(stderr, "Failed CreateEvent\n"); + goto fail; + } + + snprintf(openname, sizeof(openname), "\\\\.\\pipe\\%s", filename); + s->hcom = CreateNamedPipe(openname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | + PIPE_WAIT, + MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL); + if (s->hcom == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Failed CreateNamedPipe (%lu)\n", GetLastError()); + s->hcom = NULL; + goto fail; + } + + ZeroMemory(&ov, sizeof(ov)); + ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + ret = ConnectNamedPipe(s->hcom, &ov); + if (ret) { + fprintf(stderr, "Failed ConnectNamedPipe\n"); + goto fail; + } + + ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE); + if (!ret) { + fprintf(stderr, "Failed GetOverlappedResult\n"); + if (ov.hEvent) { + CloseHandle(ov.hEvent); + ov.hEvent = NULL; + } + goto fail; + } + + if (ov.hEvent) { + CloseHandle(ov.hEvent); + ov.hEvent = NULL; + } + qemu_add_polling_cb(win_chr_pipe_poll, s); + return 0; + + fail: + win_chr_close2(s); + return -1; +} + + +CharDriverState *qemu_chr_open_win_pipe(const char *filename) +{ + CharDriverState *chr; + WinCharState *s; + + chr = qemu_mallocz(sizeof(CharDriverState)); + if (!chr) + return NULL; + s = qemu_mallocz(sizeof(WinCharState)); + if (!s) { + free(chr); + return NULL; + } + chr->opaque = s; + chr->chr_write = win_chr_write; + chr->chr_add_read_handler = win_chr_add_read_handler; + chr->chr_close = win_chr_close; + + if (win_chr_pipe_init(s, filename) < 0) { + free(s); + free(chr); + return NULL; + } + return chr; +} + +CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) +{ + CharDriverState *chr; + WinCharState *s; + + chr = qemu_mallocz(sizeof(CharDriverState)); + if (!chr) + return NULL; + s = qemu_mallocz(sizeof(WinCharState)); + if (!s) { + free(chr); + return NULL; + } + s->hcom = fd_out; + chr->opaque = s; + chr->chr_write = win_chr_write; + chr->chr_add_read_handler = win_chr_add_read_handler; + return chr; +} + +CharDriverState *qemu_chr_open_win_file_out(const char *file_out) +{ + HANDLE fd_out; + + fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (fd_out == INVALID_HANDLE_VALUE) + return NULL; + + return qemu_chr_open_win_file(fd_out); +} +#endif + +CharDriverState *qemu_chr_open(const char *filename) +{ + const char *p; + + if (!strcmp(filename, "vc")) { + return text_console_init(&display_state); + } else if (!strcmp(filename, "null")) { + return qemu_chr_open_null(); + } else +#ifndef _WIN32 + if (strstart(filename, "file:", &p)) { + return qemu_chr_open_file_out(p); + } else if (strstart(filename, "pipe:", &p)) { + return qemu_chr_open_pipe(p); + } else if (!strcmp(filename, "pty")) { + return qemu_chr_open_pty(); + } else if (!strcmp(filename, "stdio")) { + return qemu_chr_open_stdio(); + } else +#endif +#if defined(__linux__) + if (strstart(filename, "/dev/parport", NULL)) { + return qemu_chr_open_pp(filename); + } else + if (strstart(filename, "/dev/", NULL)) { + return qemu_chr_open_tty(filename); + } else +#endif +#ifdef _WIN32 + if (strstart(filename, "COM", NULL)) { + return qemu_chr_open_win(filename); + } else + if (strstart(filename, "pipe:", &p)) { + return qemu_chr_open_win_pipe(p); + } else + if (strstart(filename, "file:", &p)) { + return qemu_chr_open_win_file_out(p); + } +#endif + { + return NULL; + } +} + +void qemu_chr_close(CharDriverState *chr) +{ + if (chr->chr_close) + chr->chr_close(chr); +} + +/***********************************************************/ +/* network device redirectors */ + +void hex_dump(FILE *f, const uint8_t *buf, int size) +{ + int len, i, j, c; + + for(i=0;i 16) + len = 16; + fprintf(f, "%08x ", i); + for(j=0;j<16;j++) { + if (j < len) + fprintf(f, " %02x", buf[i+j]); + else + fprintf(f, " "); + } + fprintf(f, " "); + for(j=0;j '~') + c = '.'; + fprintf(f, "%c", c); + } + fprintf(f, "\n"); + } +} + +static int parse_macaddr(uint8_t *macaddr, const char *p) +{ + int i; + for(i = 0; i < 6; i++) { + macaddr[i] = strtol(p, (char **)&p, 16); + if (i == 5) { + if (*p != '\0') + return -1; + } else { + if (*p != ':') + return -1; + p++; + } + } + return 0; +} + +static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) +{ + const char *p, *p1; + int len; + p = *pp; + p1 = strchr(p, sep); + if (!p1) + return -1; + len = p1 - p; + p1++; + if (buf_size > 0) { + if (len > buf_size - 1) + len = buf_size - 1; + memcpy(buf, p, len); + buf[len] = '\0'; + } + *pp = p1; + return 0; +} + +int parse_host_port(struct sockaddr_in *saddr, const char *str) +{ + char buf[512]; + struct hostent *he; + const char *p, *r; + int port; + + p = str; + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) + return -1; + saddr->sin_family = AF_INET; + if (buf[0] == '\0') { + saddr->sin_addr.s_addr = 0; + } else { + if (isdigit(buf[0])) { + if (!inet_aton(buf, &saddr->sin_addr)) + return -1; + } else { + if ((he = gethostbyname(buf)) == NULL) + return - 1; + saddr->sin_addr = *(struct in_addr *)he->h_addr; + } + } + port = strtol(p, (char **)&r, 0); + if (r == p) + return -1; + saddr->sin_port = htons(port); + return 0; +} + +/* find or alloc a new VLAN */ +VLANState *qemu_find_vlan(int id) +{ + VLANState **pvlan, *vlan; + for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) { + if (vlan->id == id) + return vlan; + } + vlan = qemu_mallocz(sizeof(VLANState)); + if (!vlan) + return NULL; + vlan->id = id; + vlan->next = NULL; + pvlan = &first_vlan; + while (*pvlan != NULL) + pvlan = &(*pvlan)->next; + *pvlan = vlan; + return vlan; +} + +VLANClientState *qemu_new_vlan_client(VLANState *vlan, + IOReadHandler *fd_read, + IOCanRWHandler *fd_can_read, + void *opaque) +{ + VLANClientState *vc, **pvc; + vc = qemu_mallocz(sizeof(VLANClientState)); + if (!vc) + return NULL; + vc->fd_read = fd_read; + vc->fd_can_read = fd_can_read; + vc->opaque = opaque; + vc->vlan = vlan; + + vc->next = NULL; + pvc = &vlan->first_client; + while (*pvc != NULL) + pvc = &(*pvc)->next; + *pvc = vc; + return vc; +} + +int qemu_can_send_packet(VLANClientState *vc1) +{ + VLANState *vlan = vc1->vlan; + VLANClientState *vc; + + for(vc = vlan->first_client; vc != NULL; vc = vc->next) { + if (vc != vc1) { + if (vc->fd_can_read && !vc->fd_can_read(vc->opaque)) + return 0; + } + } + return 1; +} + +void qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size) +{ + VLANState *vlan = vc1->vlan; + VLANClientState *vc; + +#if 0 + printf("vlan %d send:\n", vlan->id); + hex_dump(stdout, buf, size); +#endif + for(vc = vlan->first_client; vc != NULL; vc = vc->next) { + if (vc != vc1) { + vc->fd_read(vc->opaque, buf, size); + } + } +} + +#if defined(CONFIG_SLIRP) + +/* slirp network adapter */ + +static int slirp_inited; +static VLANClientState *slirp_vc; + +int slirp_can_output(void) +{ + return !slirp_vc || qemu_can_send_packet(slirp_vc); +} + +void slirp_output(const uint8_t *pkt, int pkt_len) +{ +#if 0 + printf("slirp output:\n"); + hex_dump(stdout, pkt, pkt_len); +#endif + if (!slirp_vc) + return; + qemu_send_packet(slirp_vc, pkt, pkt_len); +} + +static void slirp_receive(void *opaque, const uint8_t *buf, int size) +{ +#if 0 + printf("slirp input:\n"); + hex_dump(stdout, buf, size); +#endif + slirp_input(buf, size); +} + +static int net_slirp_init(VLANState *vlan) +{ + if (!slirp_inited) { + slirp_inited = 1; + slirp_init(); + } + slirp_vc = qemu_new_vlan_client(vlan, + slirp_receive, NULL, NULL); + snprintf(slirp_vc->info_str, sizeof(slirp_vc->info_str), "user redirector"); + return 0; +} + +static void net_slirp_redir(const char *redir_str) +{ + int is_udp; + char buf[256], *r; + const char *p; + struct in_addr guest_addr; + int host_port, guest_port; + + if (!slirp_inited) { + slirp_inited = 1; + slirp_init(); + } + + p = redir_str; + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) + goto fail; + if (!strcmp(buf, "tcp")) { + is_udp = 0; + } else if (!strcmp(buf, "udp")) { + is_udp = 1; + } else { + goto fail; + } + + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) + goto fail; + host_port = strtol(buf, &r, 0); + if (r == buf) + goto fail; + + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) + goto fail; + if (buf[0] == '\0') { + pstrcpy(buf, sizeof(buf), "10.0.2.15"); + } + if (!inet_aton(buf, &guest_addr)) + goto fail; + + guest_port = strtol(p, &r, 0); + if (r == p) + goto fail; + + if (slirp_redir(is_udp, host_port, guest_addr, guest_port) < 0) { + fprintf(stderr, "qemu: could not set up redirection\n"); + exit(1); + } + return; + fail: + fprintf(stderr, "qemu: syntax: -redir [tcp|udp]:host-port:[guest-host]:guest-port\n"); + exit(1); +} + +#ifndef _WIN32 + +char smb_dir[1024]; + +static void smb_exit(void) +{ + DIR *d; + struct dirent *de; + char filename[1024]; + + /* erase all the files in the directory */ + d = opendir(smb_dir); + for(;;) { + de = readdir(d); + if (!de) + break; + if (strcmp(de->d_name, ".") != 0 && + strcmp(de->d_name, "..") != 0) { + snprintf(filename, sizeof(filename), "%s/%s", + smb_dir, de->d_name); + unlink(filename); + } + } + closedir(d); + rmdir(smb_dir); +} + +/* automatic user mode samba server configuration */ +void net_slirp_smb(const char *exported_dir) +{ + char smb_conf[1024]; + char smb_cmdline[1024]; + FILE *f; + + if (!slirp_inited) { + slirp_inited = 1; + slirp_init(); + } + + /* XXX: better tmp dir construction */ + snprintf(smb_dir, sizeof(smb_dir), "/tmp/qemu-smb.%d", getpid()); + if (mkdir(smb_dir, 0700) < 0) { + fprintf(stderr, "qemu: could not create samba server dir '%s'\n", smb_dir); + exit(1); + } + snprintf(smb_conf, sizeof(smb_conf), "%s/%s", smb_dir, "smb.conf"); + + f = fopen(smb_conf, "w"); + if (!f) { + fprintf(stderr, "qemu: could not create samba server configuration file '%s'\n", smb_conf); + exit(1); + } + fprintf(f, + "[global]\n" + "private dir=%s\n" + "smb ports=0\n" + "socket address=127.0.0.1\n" + "pid directory=%s\n" + "lock directory=%s\n" + "log file=%s/log.smbd\n" + "smb passwd file=%s/smbpasswd\n" + "security = share\n" + "[qemu]\n" + "path=%s\n" + "read only=no\n" + "guest ok=yes\n", + smb_dir, + smb_dir, + smb_dir, + smb_dir, + smb_dir, + exported_dir + ); + fclose(f); + atexit(smb_exit); + + snprintf(smb_cmdline, sizeof(smb_cmdline), "/usr/sbin/smbd -s %s", + smb_conf); + + slirp_add_exec(0, smb_cmdline, 4, 139); +} + +#endif /* !defined(_WIN32) */ + +#endif /* CONFIG_SLIRP */ + +#if !defined(_WIN32) + +typedef struct TAPState { + VLANClientState *vc; + int fd; +} TAPState; + +static void tap_receive(void *opaque, const uint8_t *buf, int size) +{ + TAPState *s = opaque; + int ret; + for(;;) { + ret = write(s->fd, buf, size); + if (ret < 0 && (errno == EINTR || errno == EAGAIN)) { + } else { + break; + } + } +} + +static void tap_send(void *opaque) +{ + TAPState *s = opaque; + uint8_t buf[4096]; + int size; + + size = read(s->fd, buf, sizeof(buf)); + if (size > 0) { + qemu_send_packet(s->vc, buf, size); + } +} + +/* fd support */ + +static TAPState *net_tap_fd_init(VLANState *vlan, int fd) +{ + TAPState *s; + + s = qemu_mallocz(sizeof(TAPState)); + if (!s) + return NULL; + s->fd = fd; + s->vc = qemu_new_vlan_client(vlan, tap_receive, NULL, s); + qemu_set_fd_handler(s->fd, tap_send, NULL, s); + snprintf(s->vc->info_str, sizeof(s->vc->info_str), "tap: fd=%d", fd); + return s; +} + +#ifdef _BSD +static int tap_open(char *ifname, int ifname_size) +{ + int fd; + char *dev; + struct stat s; + + fd = open("/dev/tap", O_RDWR); + if (fd < 0) { + fprintf(stderr, "warning: could not open /dev/tap: no virtual network emulation\n"); + return -1; + } + + fstat(fd, &s); + dev = devname(s.st_rdev, S_IFCHR); + pstrcpy(ifname, ifname_size, dev); + + fcntl(fd, F_SETFL, O_NONBLOCK); + return fd; +} +#elif defined(__sun__) +static int tap_open(char *ifname, int ifname_size) +{ + fprintf(stderr, "warning: tap_open not yet implemented\n"); + return -1; +} +#else +static int tap_open(char *ifname, int ifname_size) +{ + struct ifreq ifr; + int fd, ret; + + fd = open("/dev/net/tun", O_RDWR); + if (fd < 0) { + fprintf(stderr, "warning: could not open /dev/net/tun: no virtual network emulation\n"); + return -1; + } + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + if (ifname[0] != '\0') + pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname); + else + pstrcpy(ifr.ifr_name, IFNAMSIZ, "tap%d"); + ret = ioctl(fd, TUNSETIFF, (void *) &ifr); + if (ret != 0) { + fprintf(stderr, "warning: could not configure /dev/net/tun: no virtual network emulation\n"); + close(fd); + return -1; + } + pstrcpy(ifname, ifname_size, ifr.ifr_name); + fcntl(fd, F_SETFL, O_NONBLOCK); + return fd; +} +#endif + +static int net_tap_init(VLANState *vlan, const char *ifname1, + const char *setup_script, const char *bridge) +{ + TAPState *s; + int pid, status, fd; + char *args[4]; + char **parg; + char ifname[128]; + + if (ifname1 != NULL) + pstrcpy(ifname, sizeof(ifname), ifname1); + else + ifname[0] = '\0'; + fd = tap_open(ifname, sizeof(ifname)); + if (fd < 0) + return -1; + + if (!setup_script) + setup_script = ""; + if (setup_script[0] != '\0') { + /* try to launch network init script */ + pid = fork(); + if (pid >= 0) { + if (pid == 0) { + parg = args; + *parg++ = (char *)setup_script; + *parg++ = ifname; + *parg++ = (char *)bridge; + *parg++ = NULL; + execv(setup_script, args); + _exit(1); + } + while (waitpid(pid, &status, 0) != pid); + if (!WIFEXITED(status) || + WEXITSTATUS(status) != 0) { + fprintf(stderr, "%s: could not launch network script\n", + setup_script); + return -1; + } + } + } + s = net_tap_fd_init(vlan, fd); + if (!s) + return -1; + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "tap: ifname=%s setup_script=%s", ifname, setup_script); + return 0; +} + +#endif /* !_WIN32 */ + +/* network connection */ +typedef struct NetSocketState { + VLANClientState *vc; + int fd; + int state; /* 0 = getting length, 1 = getting data */ + int index; + int packet_len; + uint8_t buf[4096]; + struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */ +} NetSocketState; + +typedef struct NetSocketListenState { + VLANState *vlan; + int fd; +} NetSocketListenState; + +/* XXX: we consider we can send the whole packet without blocking */ +static void net_socket_receive(void *opaque, const uint8_t *buf, int size) +{ + NetSocketState *s = opaque; + uint32_t len; + len = htonl(size); + + send_all(s->fd, (const uint8_t *)&len, sizeof(len)); + send_all(s->fd, buf, size); +} + +static void net_socket_receive_dgram(void *opaque, const uint8_t *buf, int size) +{ + NetSocketState *s = opaque; + sendto(s->fd, buf, size, 0, + (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst)); +} + +static void net_socket_send(void *opaque) +{ + NetSocketState *s = opaque; + int l, size, err; + uint8_t buf1[4096]; + const uint8_t *buf; + + size = recv(s->fd, buf1, sizeof(buf1), 0); + if (size < 0) { + err = socket_error(); + if (err != EWOULDBLOCK) + goto eoc; + } else if (size == 0) { + /* end of connection */ + eoc: + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + closesocket(s->fd); + return; + } + buf = buf1; + while (size > 0) { + /* reassemble a packet from the network */ + switch(s->state) { + case 0: + l = 4 - s->index; + if (l > size) + l = size; + memcpy(s->buf + s->index, buf, l); + buf += l; + size -= l; + s->index += l; + if (s->index == 4) { + /* got length */ + s->packet_len = ntohl(*(uint32_t *)s->buf); + s->index = 0; + s->state = 1; + } + break; + case 1: + l = s->packet_len - s->index; + if (l > size) + l = size; + memcpy(s->buf + s->index, buf, l); + s->index += l; + buf += l; + size -= l; + if (s->index >= s->packet_len) { + qemu_send_packet(s->vc, s->buf, s->packet_len); + s->index = 0; + s->state = 0; + } + break; + } + } +} + +static void net_socket_send_dgram(void *opaque) +{ + NetSocketState *s = opaque; + int size; + + size = recv(s->fd, s->buf, sizeof(s->buf), 0); + if (size < 0) + return; + if (size == 0) { + /* end of connection */ + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + return; + } + qemu_send_packet(s->vc, s->buf, size); +} + +static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) +{ + struct ip_mreq imr; + int fd; + int val, ret; + if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) { + fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) does not contain a multicast address\n", + inet_ntoa(mcastaddr->sin_addr), + (int)ntohl(mcastaddr->sin_addr.s_addr)); + return -1; + + } + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("socket(PF_INET, SOCK_DGRAM)"); + return -1; + } + + val = 1; + ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (const char *)&val, sizeof(val)); + if (ret < 0) { + perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)"); + goto fail; + } + + ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr)); + if (ret < 0) { + perror("bind"); + goto fail; + } + + /* Add host to multicast group */ + imr.imr_multiaddr = mcastaddr->sin_addr; + imr.imr_interface.s_addr = htonl(INADDR_ANY); + + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (const char *)&imr, sizeof(struct ip_mreq)); + if (ret < 0) { + perror("setsockopt(IP_ADD_MEMBERSHIP)"); + goto fail; + } + + /* Force mcast msgs to loopback (eg. several QEMUs in same host */ + val = 1; + ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + (const char *)&val, sizeof(val)); + if (ret < 0) { + perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)"); + goto fail; + } + + socket_set_nonblock(fd); + return fd; +fail: + if (fd>=0) close(fd); + return -1; +} + +static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, int fd, + int is_connected) +{ + struct sockaddr_in saddr; + int newfd; + socklen_t saddr_len; + NetSocketState *s; + + /* fd passed: multicast: "learn" dgram_dst address from bound address and save it + * Because this may be "shared" socket from a "master" process, datagrams would be recv() + * by ONLY ONE process: we must "clone" this dgram socket --jjo + */ + + if (is_connected) { + if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) { + /* must be bound */ + if (saddr.sin_addr.s_addr==0) { + fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n", + fd); + return NULL; + } + /* clone dgram socket */ + newfd = net_socket_mcast_create(&saddr); + if (newfd < 0) { + /* error already reported by net_socket_mcast_create() */ + close(fd); + return NULL; + } + /* clone newfd to fd, close newfd */ + dup2(newfd, fd); + close(newfd); + + } else { + fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n", + fd, strerror(errno)); + return NULL; + } + } + + s = qemu_mallocz(sizeof(NetSocketState)); + if (!s) + return NULL; + s->fd = fd; + + s->vc = qemu_new_vlan_client(vlan, net_socket_receive_dgram, NULL, s); + qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s); + + /* mcast: save bound address as dst */ + if (is_connected) s->dgram_dst=saddr; + + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "socket: fd=%d (%s mcast=%s:%d)", + fd, is_connected? "cloned" : "", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + return s; +} + +static void net_socket_connect(void *opaque) +{ + NetSocketState *s = opaque; + qemu_set_fd_handler(s->fd, net_socket_send, NULL, s); +} + +static NetSocketState *net_socket_fd_init_stream(VLANState *vlan, int fd, + int is_connected) +{ + NetSocketState *s; + s = qemu_mallocz(sizeof(NetSocketState)); + if (!s) + return NULL; + s->fd = fd; + s->vc = qemu_new_vlan_client(vlan, + net_socket_receive, NULL, s); + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "socket: fd=%d", fd); + if (is_connected) { + net_socket_connect(s); + } else { + qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s); + } + return s; +} + +static NetSocketState *net_socket_fd_init(VLANState *vlan, int fd, + int is_connected) +{ + int so_type=-1, optlen=sizeof(so_type); + + if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type, &optlen)< 0) { + fprintf(stderr, "qemu: error: setsockopt(SO_TYPE) for fd=%d failed\n", fd); + return NULL; + } + switch(so_type) { + case SOCK_DGRAM: + return net_socket_fd_init_dgram(vlan, fd, is_connected); + case SOCK_STREAM: + return net_socket_fd_init_stream(vlan, fd, is_connected); + default: + /* who knows ... this could be a eg. a pty, do warn and continue as stream */ + fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd); + return net_socket_fd_init_stream(vlan, fd, is_connected); + } + return NULL; +} + +static void net_socket_accept(void *opaque) +{ + NetSocketListenState *s = opaque; + NetSocketState *s1; + struct sockaddr_in saddr; + socklen_t len; + int fd; + + for(;;) { + len = sizeof(saddr); + fd = accept(s->fd, (struct sockaddr *)&saddr, &len); + if (fd < 0 && errno != EINTR) { + return; + } else if (fd >= 0) { + break; + } + } + s1 = net_socket_fd_init(s->vlan, fd, 1); + if (!s1) { + close(fd); + } else { + snprintf(s1->vc->info_str, sizeof(s1->vc->info_str), + "socket: connection from %s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + } +} + +static int net_socket_listen_init(VLANState *vlan, const char *host_str) +{ + NetSocketListenState *s; + int fd, val, ret; + struct sockaddr_in saddr; + + if (parse_host_port(&saddr, host_str) < 0) + return -1; + + s = qemu_mallocz(sizeof(NetSocketListenState)); + if (!s) + return -1; + + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + socket_set_nonblock(fd); + + /* allow fast reuse */ + val = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)); + + ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)); + if (ret < 0) { + perror("bind"); + return -1; + } + ret = listen(fd, 0); + if (ret < 0) { + perror("listen"); + return -1; + } + s->vlan = vlan; + s->fd = fd; + qemu_set_fd_handler(fd, net_socket_accept, NULL, s); + return 0; +} + +static int net_socket_connect_init(VLANState *vlan, const char *host_str) +{ + NetSocketState *s; + int fd, connected, ret, err; + struct sockaddr_in saddr; + + if (parse_host_port(&saddr, host_str) < 0) + return -1; + + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + socket_set_nonblock(fd); + + connected = 0; + for(;;) { + ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); + if (ret < 0) { + err = socket_error(); + if (err == EINTR || err == EWOULDBLOCK) { + } else if (err == EINPROGRESS) { + break; + } else { + perror("connect"); + closesocket(fd); + return -1; + } + } else { + connected = 1; + break; + } + } + s = net_socket_fd_init(vlan, fd, connected); + if (!s) + return -1; + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "socket: connect to %s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + return 0; +} + +static int net_socket_mcast_init(VLANState *vlan, const char *host_str) +{ + NetSocketState *s; + int fd; + struct sockaddr_in saddr; + + if (parse_host_port(&saddr, host_str) < 0) + return -1; + + + fd = net_socket_mcast_create(&saddr); + if (fd < 0) + return -1; + + s = net_socket_fd_init(vlan, fd, 0); + if (!s) + return -1; + + s->dgram_dst = saddr; + + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "socket: mcast=%s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + return 0; + +} + +static int get_param_value(char *buf, int buf_size, + const char *tag, const char *str) +{ + const char *p; + char *q; + char option[128]; + + p = str; + for(;;) { + q = option; + while (*p != '\0' && *p != '=') { + if ((q - option) < sizeof(option) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + if (*p != '=') + break; + p++; + if (!strcmp(tag, option)) { + q = buf; + while (*p != '\0' && *p != ',') { + if ((q - buf) < buf_size - 1) + *q++ = *p; + p++; + } + *q = '\0'; + return q - buf; + } else { + while (*p != '\0' && *p != ',') { + p++; + } + } + if (*p != ',') + break; + p++; + } + return 0; +} + +int net_client_init(const char *str) +{ + const char *p; + char *q; + char device[64]; + char buf[1024]; + int vlan_id, ret; + VLANState *vlan; + + p = str; + q = device; + while (*p != '\0' && *p != ',') { + if ((q - device) < sizeof(device) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + if (*p == ',') + p++; + vlan_id = 0; + if (get_param_value(buf, sizeof(buf), "vlan", p)) { + vlan_id = strtol(buf, NULL, 0); + } + vlan = qemu_find_vlan(vlan_id); + if (!vlan) { + fprintf(stderr, "Could not create vlan %d\n", vlan_id); + return -1; + } + if (!strcmp(device, "nic")) { + NICInfo *nd; + uint8_t *macaddr; + + if (nb_nics >= MAX_NICS) { + fprintf(stderr, "Too Many NICs\n"); + return -1; + } + nd = &nd_table[nb_nics]; + macaddr = nd->macaddr; + macaddr[0] = 0x52; + macaddr[1] = 0x54; + macaddr[2] = 0x00; + macaddr[3] = 0x12; + macaddr[4] = 0x34; + macaddr[5] = 0x56 + nb_nics; + + if (get_param_value(buf, sizeof(buf), "macaddr", p)) { + if (parse_macaddr(macaddr, buf) < 0) { + fprintf(stderr, "invalid syntax for ethernet address\n"); + return -1; + } + } + if (get_param_value(buf, sizeof(buf), "model", p)) { + nd->model = strdup(buf); + } + nd->vlan = vlan; + nb_nics++; + ret = 0; + } else + if (!strcmp(device, "none")) { + /* does nothing. It is needed to signal that no network cards + are wanted */ + ret = 0; + } else +#ifdef CONFIG_SLIRP + if (!strcmp(device, "user")) { + if (get_param_value(buf, sizeof(buf), "hostname", p)) { + pstrcpy(slirp_hostname, sizeof(slirp_hostname), buf); + } + ret = net_slirp_init(vlan); + } else +#endif +#ifdef _WIN32 + if (!strcmp(device, "tap")) { + char ifname[64]; + if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) { + fprintf(stderr, "tap: no interface name\n"); + return -1; + } + ret = tap_win32_init(vlan, ifname); + } else +#else + if (!strcmp(device, "tap")) { + char ifname[64]; + char setup_script[1024]; + char bridge[16]; + int fd; + if (get_param_value(buf, sizeof(buf), "fd", p) > 0) { + fd = strtol(buf, NULL, 0); + ret = -1; + if (net_tap_fd_init(vlan, fd)) + ret = 0; + } else { + get_param_value(ifname, sizeof(ifname), "ifname", p); + if (get_param_value(setup_script, sizeof(setup_script), "script", p) == 0) { + pstrcpy(setup_script, sizeof(setup_script), DEFAULT_NETWORK_SCRIPT); + } + if (get_param_value(bridge, sizeof(bridge), "bridge", p) == 0) { + pstrcpy(bridge, sizeof(bridge), DEFAULT_BRIDGE); + } + ret = net_tap_init(vlan, ifname, setup_script, bridge); + } + } else +#endif + if (!strcmp(device, "socket")) { + if (get_param_value(buf, sizeof(buf), "fd", p) > 0) { + int fd; + fd = strtol(buf, NULL, 0); + ret = -1; + if (net_socket_fd_init(vlan, fd, 1)) + ret = 0; + } else if (get_param_value(buf, sizeof(buf), "listen", p) > 0) { + ret = net_socket_listen_init(vlan, buf); + } else if (get_param_value(buf, sizeof(buf), "connect", p) > 0) { + ret = net_socket_connect_init(vlan, buf); + } else if (get_param_value(buf, sizeof(buf), "mcast", p) > 0) { + ret = net_socket_mcast_init(vlan, buf); + } else { + fprintf(stderr, "Unknown socket options: %s\n", p); + return -1; + } + } else + { + fprintf(stderr, "Unknown network device: %s\n", device); + return -1; + } + if (ret < 0) { + fprintf(stderr, "Could not initialize device '%s'\n", device); + } + + return ret; +} + +void do_info_network(void) +{ + VLANState *vlan; + VLANClientState *vc; + + for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) { + term_printf("VLAN %d devices:\n", vlan->id); + for(vc = vlan->first_client; vc != NULL; vc = vc->next) + term_printf(" %s\n", vc->info_str); + } +} + +/***********************************************************/ +/* USB devices */ + +static int usb_device_add(const char *devname) +{ + const char *p; + USBDevice *dev; + int i; + + if (!vm_usb_hub) + return -1; + for(i = 0;i < MAX_VM_USB_PORTS; i++) { + if (!vm_usb_ports[i]->dev) + break; + } + if (i == MAX_VM_USB_PORTS) + return -1; + + if (strstart(devname, "host:", &p)) { + dev = usb_host_device_open(p); + if (!dev) + return -1; + } else if (!strcmp(devname, "mouse")) { + dev = usb_mouse_init(); + if (!dev) + return -1; + } else if (!strcmp(devname, "tablet")) { + dev = usb_tablet_init(); + if (!dev) + return -1; + } else { + return -1; + } + usb_attach(vm_usb_ports[i], dev); + return 0; +} + +static int usb_device_del(const char *devname) +{ + USBDevice *dev; + int bus_num, addr, i; + const char *p; + + if (!vm_usb_hub) + return -1; + + p = strchr(devname, '.'); + if (!p) + return -1; + bus_num = strtoul(devname, NULL, 0); + addr = strtoul(p + 1, NULL, 0); + if (bus_num != 0) + return -1; + for(i = 0;i < MAX_VM_USB_PORTS; i++) { + dev = vm_usb_ports[i]->dev; + if (dev && dev->addr == addr) + break; + } + if (i == MAX_VM_USB_PORTS) + return -1; + usb_attach(vm_usb_ports[i], NULL); + return 0; +} + +void do_usb_add(const char *devname) +{ + int ret; + ret = usb_device_add(devname); + if (ret < 0) + term_printf("Could not add USB device '%s'\n", devname); +} + +void do_usb_del(const char *devname) +{ + int ret; + ret = usb_device_del(devname); + if (ret < 0) + term_printf("Could not remove USB device '%s'\n", devname); +} + +void usb_info(void) +{ + USBDevice *dev; + int i; + const char *speed_str; + + if (!vm_usb_hub) { + term_printf("USB support not enabled\n"); + return; + } + + for(i = 0; i < MAX_VM_USB_PORTS; i++) { + dev = vm_usb_ports[i]->dev; + if (dev) { + term_printf("Hub port %d:\n", i); + switch(dev->speed) { + case USB_SPEED_LOW: + speed_str = "1.5"; + break; + case USB_SPEED_FULL: + speed_str = "12"; + break; + case USB_SPEED_HIGH: + speed_str = "480"; + break; + default: + speed_str = "?"; + break; + } + term_printf(" Device %d.%d, speed %s Mb/s\n", + 0, dev->addr, speed_str); + } + } +} + +/***********************************************************/ +/* pid file */ + +static char *pid_filename; + +/* Remove PID file. Called on normal exit */ + +static void remove_pidfile(void) +{ + unlink (pid_filename); +} + +static void create_pidfile(const char *filename) +{ + struct stat pidstat; + FILE *f; + + /* Try to write our PID to the named file */ + if (stat(filename, &pidstat) < 0) { + if (errno == ENOENT) { + if ((f = fopen (filename, "w")) == NULL) { + perror("Opening pidfile"); + exit(1); + } + fprintf(f, "%d\n", getpid()); + fclose(f); + pid_filename = qemu_strdup(filename); + if (!pid_filename) { + fprintf(stderr, "Could not save PID filename"); + exit(1); + } + atexit(remove_pidfile); + } + } else { + fprintf(stderr, "%s already exists. Remove it and try again.\n", + filename); + exit(1); + } +} + +/***********************************************************/ +/* dumb display */ + +static void dumb_update(DisplayState *ds, int x, int y, int w, int h) +{ +} + +static void dumb_resize(DisplayState *ds, int w, int h) +{ +} + +static void dumb_refresh(DisplayState *ds) +{ + vga_hw_update(); +} + +void dumb_display_init(DisplayState *ds) +{ + ds->data = NULL; + ds->linesize = 0; + ds->depth = 0; + ds->dpy_update = dumb_update; + ds->dpy_resize = dumb_resize; + ds->dpy_refresh = dumb_refresh; +} + +#if !defined(CONFIG_SOFTMMU) +/***********************************************************/ +/* cpu signal handler */ +static void host_segv_handler(int host_signum, siginfo_t *info, + void *puc) +{ + if (cpu_signal_handler(host_signum, info, puc)) + return; + if (stdio_nb_clients > 0) + term_exit(); + abort(); +} +#endif + +/***********************************************************/ +/* I/O handling */ + +#define MAX_IO_HANDLERS 64 + +typedef struct IOHandlerRecord { + int fd; + IOCanRWHandler *fd_read_poll; + IOHandler *fd_read; + IOHandler *fd_write; + void *opaque; + /* temporary data */ + struct pollfd *ufd; + struct IOHandlerRecord *next; +} IOHandlerRecord; + +static IOHandlerRecord *first_io_handler; + +/* XXX: fd_read_poll should be suppressed, but an API change is + necessary in the character devices to suppress fd_can_read(). */ +int qemu_set_fd_handler2(int fd, + IOCanRWHandler *fd_read_poll, + IOHandler *fd_read, + IOHandler *fd_write, + void *opaque) +{ + IOHandlerRecord **pioh, *ioh; + + if (!fd_read && !fd_write) { + pioh = &first_io_handler; + for(;;) { + ioh = *pioh; + if (ioh == NULL) + break; + if (ioh->fd == fd) { + *pioh = ioh->next; + qemu_free(ioh); + break; + } + pioh = &ioh->next; + } + } else { + for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { + if (ioh->fd == fd) + goto found; + } + ioh = qemu_mallocz(sizeof(IOHandlerRecord)); + if (!ioh) + return -1; + ioh->next = first_io_handler; + first_io_handler = ioh; + found: + ioh->fd = fd; + ioh->fd_read_poll = fd_read_poll; + ioh->fd_read = fd_read; + ioh->fd_write = fd_write; + ioh->opaque = opaque; + } + return 0; +} + +int qemu_set_fd_handler(int fd, + IOHandler *fd_read, + IOHandler *fd_write, + void *opaque) +{ + return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque); +} + +/***********************************************************/ +/* Polling handling */ + +typedef struct PollingEntry { + PollingFunc *func; + void *opaque; + struct PollingEntry *next; +} PollingEntry; + +static PollingEntry *first_polling_entry; + +int qemu_add_polling_cb(PollingFunc *func, void *opaque) +{ + PollingEntry **ppe, *pe; + pe = qemu_mallocz(sizeof(PollingEntry)); + if (!pe) + return -1; + pe->func = func; + pe->opaque = opaque; + for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next); + *ppe = pe; + return 0; +} + +void qemu_del_polling_cb(PollingFunc *func, void *opaque) +{ + PollingEntry **ppe, *pe; + for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next) { + pe = *ppe; + if (pe->func == func && pe->opaque == opaque) { + *ppe = pe->next; + qemu_free(pe); + break; + } + } +} + +/***********************************************************/ +/* savevm/loadvm support */ + +void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) +{ + fwrite(buf, 1, size, f); +} + +void qemu_put_byte(QEMUFile *f, int v) +{ + fputc(v, f); +} + +void qemu_put_be16(QEMUFile *f, unsigned int v) +{ + qemu_put_byte(f, v >> 8); + qemu_put_byte(f, v); +} + +void qemu_put_be32(QEMUFile *f, unsigned int v) +{ + qemu_put_byte(f, v >> 24); + qemu_put_byte(f, v >> 16); + qemu_put_byte(f, v >> 8); + qemu_put_byte(f, v); +} + +void qemu_put_be64(QEMUFile *f, uint64_t v) +{ + qemu_put_be32(f, v >> 32); + qemu_put_be32(f, v); +} + +int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size) +{ + return fread(buf, 1, size, f); +} + +int qemu_get_byte(QEMUFile *f) +{ + int v; + v = fgetc(f); + if (v == EOF) + return 0; + else + return v; +} + +unsigned int qemu_get_be16(QEMUFile *f) +{ + unsigned int v; + v = qemu_get_byte(f) << 8; + v |= qemu_get_byte(f); + return v; +} + +unsigned int qemu_get_be32(QEMUFile *f) +{ + unsigned int v; + v = qemu_get_byte(f) << 24; + v |= qemu_get_byte(f) << 16; + v |= qemu_get_byte(f) << 8; + v |= qemu_get_byte(f); + return v; +} + +uint64_t qemu_get_be64(QEMUFile *f) +{ + uint64_t v; + v = (uint64_t)qemu_get_be32(f) << 32; + v |= qemu_get_be32(f); + return v; +} + +int64_t qemu_ftell(QEMUFile *f) +{ + return ftell(f); +} + +int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence) +{ + if (fseek(f, pos, whence) < 0) + return -1; + return ftell(f); +} + +typedef struct SaveStateEntry { + char idstr[256]; + int instance_id; + int version_id; + SaveStateHandler *save_state; + LoadStateHandler *load_state; + void *opaque; + struct SaveStateEntry *next; +} SaveStateEntry; + +static SaveStateEntry *first_se; + +int register_savevm(const char *idstr, + int instance_id, + int version_id, + SaveStateHandler *save_state, + LoadStateHandler *load_state, + void *opaque) +{ + SaveStateEntry *se, **pse; + + se = qemu_malloc(sizeof(SaveStateEntry)); + if (!se) + return -1; + pstrcpy(se->idstr, sizeof(se->idstr), idstr); + se->instance_id = instance_id; + se->version_id = version_id; + se->save_state = save_state; + se->load_state = load_state; + se->opaque = opaque; + se->next = NULL; + + /* add at the end of list */ + pse = &first_se; + while (*pse != NULL) + pse = &(*pse)->next; + *pse = se; + return 0; +} + +#define QEMU_VM_FILE_MAGIC 0x5145564d +#define QEMU_VM_FILE_VERSION 0x00000001 + +int qemu_savevm(const char *filename) +{ + SaveStateEntry *se; + QEMUFile *f; + int len, len_pos, cur_pos, saved_vm_running, ret; + + saved_vm_running = vm_running; + vm_stop(0); + + f = fopen(filename, "wb"); + if (!f) { + ret = -1; + goto the_end; + } + + qemu_put_be32(f, QEMU_VM_FILE_MAGIC); + qemu_put_be32(f, QEMU_VM_FILE_VERSION); + + for(se = first_se; se != NULL; se = se->next) { + /* ID string */ + len = strlen(se->idstr); + qemu_put_byte(f, len); + qemu_put_buffer(f, se->idstr, len); + + qemu_put_be32(f, se->instance_id); + qemu_put_be32(f, se->version_id); + + /* record size: filled later */ + len_pos = ftell(f); + qemu_put_be32(f, 0); + + se->save_state(f, se->opaque); + + /* fill record size */ + cur_pos = ftell(f); + len = ftell(f) - len_pos - 4; + fseek(f, len_pos, SEEK_SET); + qemu_put_be32(f, len); + fseek(f, cur_pos, SEEK_SET); + } + + fclose(f); + ret = 0; + the_end: + if (saved_vm_running) + vm_start(); + return ret; +} + +static SaveStateEntry *find_se(const char *idstr, int instance_id) +{ + SaveStateEntry *se; + + for(se = first_se; se != NULL; se = se->next) { + if (!strcmp(se->idstr, idstr) && + instance_id == se->instance_id) + return se; + } + return NULL; +} + +int qemu_loadvm(const char *filename) +{ + SaveStateEntry *se; + QEMUFile *f; + int len, cur_pos, ret, instance_id, record_len, version_id; + int saved_vm_running; + unsigned int v; + char idstr[256]; + + saved_vm_running = vm_running; + vm_stop(0); + + f = fopen(filename, "rb"); + if (!f) { + ret = -1; + goto the_end; + } + + v = qemu_get_be32(f); + if (v != QEMU_VM_FILE_MAGIC) + goto fail; + v = qemu_get_be32(f); + if (v != QEMU_VM_FILE_VERSION) { + fail: + fclose(f); + ret = -1; + goto the_end; + } + for(;;) { + len = qemu_get_byte(f); + if (feof(f)) + break; + qemu_get_buffer(f, idstr, len); + idstr[len] = '\0'; + instance_id = qemu_get_be32(f); + version_id = qemu_get_be32(f); + record_len = qemu_get_be32(f); +#if 0 + printf("idstr=%s instance=0x%x version=%d len=%d\n", + idstr, instance_id, version_id, record_len); +#endif + cur_pos = ftell(f); + se = find_se(idstr, instance_id); + if (!se) { + fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n", + instance_id, idstr); + } else { + ret = se->load_state(f, se->opaque, version_id); + if (ret < 0) { + fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n", + instance_id, idstr); + } + } + /* always seek to exact end of record */ + qemu_fseek(f, cur_pos + record_len, SEEK_SET); + } + fclose(f); + ret = 0; + the_end: + if (saved_vm_running) + vm_start(); + return ret; +} + +#ifndef CONFIG_DM +/***********************************************************/ +/* cpu save/restore */ + +#if defined(TARGET_I386) + +static void cpu_put_seg(QEMUFile *f, SegmentCache *dt) +{ + qemu_put_be32(f, dt->selector); + qemu_put_betl(f, dt->base); + qemu_put_be32(f, dt->limit); + qemu_put_be32(f, dt->flags); +} + +static void cpu_get_seg(QEMUFile *f, SegmentCache *dt) +{ + dt->selector = qemu_get_be32(f); + dt->base = qemu_get_betl(f); + dt->limit = qemu_get_be32(f); + dt->flags = qemu_get_be32(f); +} + +void cpu_save(QEMUFile *f, void *opaque) +{ + CPUState *env = opaque; + uint16_t fptag, fpus, fpuc, fpregs_format; + uint32_t hflags; + int i; + + for(i = 0; i < CPU_NB_REGS; i++) + qemu_put_betls(f, &env->regs[i]); + qemu_put_betls(f, &env->eip); + qemu_put_betls(f, &env->eflags); + hflags = env->hflags; /* XXX: suppress most of the redundant hflags */ + qemu_put_be32s(f, &hflags); + + /* FPU */ + fpuc = env->fpuc; + fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + fptag = 0; + for(i = 0; i < 8; i++) { + fptag |= ((!env->fptags[i]) << i); + } + + qemu_put_be16s(f, &fpuc); + qemu_put_be16s(f, &fpus); + qemu_put_be16s(f, &fptag); + +#ifdef USE_X86LDOUBLE + fpregs_format = 0; +#else + fpregs_format = 1; +#endif + qemu_put_be16s(f, &fpregs_format); + + for(i = 0; i < 8; i++) { +#ifdef USE_X86LDOUBLE + { + uint64_t mant; + uint16_t exp; + /* we save the real CPU data (in case of MMX usage only 'mant' + contains the MMX register */ + cpu_get_fp80(&mant, &exp, env->fpregs[i].d); + qemu_put_be64(f, mant); + qemu_put_be16(f, exp); + } +#else + /* if we use doubles for float emulation, we save the doubles to + avoid losing information in case of MMX usage. It can give + problems if the image is restored on a CPU where long + doubles are used instead. */ + qemu_put_be64(f, env->fpregs[i].mmx.MMX_Q(0)); +#endif + } + + for(i = 0; i < 6; i++) + cpu_put_seg(f, &env->segs[i]); + cpu_put_seg(f, &env->ldt); + cpu_put_seg(f, &env->tr); + cpu_put_seg(f, &env->gdt); + cpu_put_seg(f, &env->idt); + + qemu_put_be32s(f, &env->sysenter_cs); + qemu_put_be32s(f, &env->sysenter_esp); + qemu_put_be32s(f, &env->sysenter_eip); + + qemu_put_betls(f, &env->cr[0]); + qemu_put_betls(f, &env->cr[2]); + qemu_put_betls(f, &env->cr[3]); + qemu_put_betls(f, &env->cr[4]); + + for(i = 0; i < 8; i++) + qemu_put_betls(f, &env->dr[i]); + + /* MMU */ + qemu_put_be32s(f, &env->a20_mask); + + /* XMM */ + qemu_put_be32s(f, &env->mxcsr); + for(i = 0; i < CPU_NB_REGS; i++) { + qemu_put_be64s(f, &env->xmm_regs[i].XMM_Q(0)); + qemu_put_be64s(f, &env->xmm_regs[i].XMM_Q(1)); + } + +#ifdef TARGET_X86_64 + qemu_put_be64s(f, &env->efer); + qemu_put_be64s(f, &env->star); + qemu_put_be64s(f, &env->lstar); + qemu_put_be64s(f, &env->cstar); + qemu_put_be64s(f, &env->fmask); + qemu_put_be64s(f, &env->kernelgsbase); +#endif +} + +#ifdef USE_X86LDOUBLE +/* XXX: add that in a FPU generic layer */ +union x86_longdouble { + uint64_t mant; + uint16_t exp; +}; + +#define MANTD1(fp) (fp & ((1LL << 52) - 1)) +#define EXPBIAS1 1023 +#define EXPD1(fp) ((fp >> 52) & 0x7FF) +#define SIGND1(fp) ((fp >> 32) & 0x80000000) + +static void fp64_to_fp80(union x86_longdouble *p, uint64_t temp) +{ + int e; + /* mantissa */ + p->mant = (MANTD1(temp) << 11) | (1LL << 63); + /* exponent + sign */ + e = EXPD1(temp) - EXPBIAS1 + 16383; + e |= SIGND1(temp) >> 16; + p->exp = e; +} +#endif + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + CPUState *env = opaque; + int i, guess_mmx; + uint32_t hflags; + uint16_t fpus, fpuc, fptag, fpregs_format; + + if (version_id != 3) + return -EINVAL; + for(i = 0; i < CPU_NB_REGS; i++) + qemu_get_betls(f, &env->regs[i]); + qemu_get_betls(f, &env->eip); + qemu_get_betls(f, &env->eflags); + qemu_get_be32s(f, &hflags); + + qemu_get_be16s(f, &fpuc); + qemu_get_be16s(f, &fpus); + qemu_get_be16s(f, &fptag); + qemu_get_be16s(f, &fpregs_format); + + /* NOTE: we cannot always restore the FPU state if the image come + from a host with a different 'USE_X86LDOUBLE' define. We guess + if we are in an MMX state to restore correctly in that case. */ + guess_mmx = ((fptag == 0xff) && (fpus & 0x3800) == 0); + for(i = 0; i < 8; i++) { + uint64_t mant; + uint16_t exp; + + switch(fpregs_format) { + case 0: + mant = qemu_get_be64(f); + exp = qemu_get_be16(f); +#ifdef USE_X86LDOUBLE + env->fpregs[i].d = cpu_set_fp80(mant, exp); +#else + /* difficult case */ + if (guess_mmx) + env->fpregs[i].mmx.MMX_Q(0) = mant; + else + env->fpregs[i].d = cpu_set_fp80(mant, exp); +#endif + break; + case 1: + mant = qemu_get_be64(f); +#ifdef USE_X86LDOUBLE + { + union x86_longdouble *p; + /* difficult case */ + p = (void *)&env->fpregs[i]; + if (guess_mmx) { + p->mant = mant; + p->exp = 0xffff; + } else { + fp64_to_fp80(p, mant); + } + } +#else + env->fpregs[i].mmx.MMX_Q(0) = mant; +#endif + break; + default: + return -EINVAL; + } + } + + env->fpuc = fpuc; + /* XXX: restore FPU round state */ + env->fpstt = (fpus >> 11) & 7; + env->fpus = fpus & ~0x3800; + fptag ^= 0xff; + for(i = 0; i < 8; i++) { + env->fptags[i] = (fptag >> i) & 1; + } + + for(i = 0; i < 6; i++) + cpu_get_seg(f, &env->segs[i]); + cpu_get_seg(f, &env->ldt); + cpu_get_seg(f, &env->tr); + cpu_get_seg(f, &env->gdt); + cpu_get_seg(f, &env->idt); + + qemu_get_be32s(f, &env->sysenter_cs); + qemu_get_be32s(f, &env->sysenter_esp); + qemu_get_be32s(f, &env->sysenter_eip); + + qemu_get_betls(f, &env->cr[0]); + qemu_get_betls(f, &env->cr[2]); + qemu_get_betls(f, &env->cr[3]); + qemu_get_betls(f, &env->cr[4]); + + for(i = 0; i < 8; i++) + qemu_get_betls(f, &env->dr[i]); + + /* MMU */ + qemu_get_be32s(f, &env->a20_mask); + + qemu_get_be32s(f, &env->mxcsr); + for(i = 0; i < CPU_NB_REGS; i++) { + qemu_get_be64s(f, &env->xmm_regs[i].XMM_Q(0)); + qemu_get_be64s(f, &env->xmm_regs[i].XMM_Q(1)); + } + +#ifdef TARGET_X86_64 + qemu_get_be64s(f, &env->efer); + qemu_get_be64s(f, &env->star); + qemu_get_be64s(f, &env->lstar); + qemu_get_be64s(f, &env->cstar); + qemu_get_be64s(f, &env->fmask); + qemu_get_be64s(f, &env->kernelgsbase); +#endif + + /* XXX: compute hflags from scratch, except for CPL and IIF */ + env->hflags = hflags; + tlb_flush(env, 1); + return 0; +} + +#elif defined(TARGET_PPC) +void cpu_save(QEMUFile *f, void *opaque) +{ +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + return 0; +} + +#elif defined(TARGET_MIPS) +void cpu_save(QEMUFile *f, void *opaque) +{ +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + return 0; +} + +#elif defined(TARGET_SPARC) +void cpu_save(QEMUFile *f, void *opaque) +{ + CPUState *env = opaque; + int i; + uint32_t tmp; + + for(i = 0; i < 8; i++) + qemu_put_betls(f, &env->gregs[i]); + for(i = 0; i < NWINDOWS * 16; i++) + qemu_put_betls(f, &env->regbase[i]); + + /* FPU */ + for(i = 0; i < TARGET_FPREGS; i++) { + union { + TARGET_FPREG_T f; + target_ulong i; + } u; + u.f = env->fpr[i]; + qemu_put_betl(f, u.i); + } + + qemu_put_betls(f, &env->pc); + qemu_put_betls(f, &env->npc); + qemu_put_betls(f, &env->y); + tmp = GET_PSR(env); + qemu_put_be32(f, tmp); + qemu_put_betls(f, &env->fsr); + qemu_put_betls(f, &env->tbr); +#ifndef TARGET_SPARC64 + qemu_put_be32s(f, &env->wim); + /* MMU */ + for(i = 0; i < 16; i++) + qemu_put_be32s(f, &env->mmuregs[i]); +#endif +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + CPUState *env = opaque; + int i; + uint32_t tmp; + + for(i = 0; i < 8; i++) + qemu_get_betls(f, &env->gregs[i]); + for(i = 0; i < NWINDOWS * 16; i++) + qemu_get_betls(f, &env->regbase[i]); + + /* FPU */ + for(i = 0; i < TARGET_FPREGS; i++) { + union { + TARGET_FPREG_T f; + target_ulong i; + } u; + u.i = qemu_get_betl(f); + env->fpr[i] = u.f; + } + + qemu_get_betls(f, &env->pc); + qemu_get_betls(f, &env->npc); + qemu_get_betls(f, &env->y); + tmp = qemu_get_be32(f); + env->cwp = 0; /* needed to ensure that the wrapping registers are + correctly updated */ + PUT_PSR(env, tmp); + qemu_get_betls(f, &env->fsr); + qemu_get_betls(f, &env->tbr); +#ifndef TARGET_SPARC64 + qemu_get_be32s(f, &env->wim); + /* MMU */ + for(i = 0; i < 16; i++) + qemu_get_be32s(f, &env->mmuregs[i]); +#endif + tlb_flush(env, 1); + return 0; +} + +#elif defined(TARGET_ARM) + +/* ??? Need to implement these. */ +void cpu_save(QEMUFile *f, void *opaque) +{ +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + return 0; +} + +#else + +#warning No CPU save/restore functions + +#endif + +/***********************************************************/ +/* ram save/restore */ + +/* we just avoid storing empty pages */ +static void ram_put_page(QEMUFile *f, const uint8_t *buf, int len) +{ + int i, v; + + v = buf[0]; + for(i = 1; i < len; i++) { + if (buf[i] != v) + goto normal_save; + } + qemu_put_byte(f, 1); + qemu_put_byte(f, v); + return; + normal_save: + qemu_put_byte(f, 0); + qemu_put_buffer(f, buf, len); +} + +static int ram_get_page(QEMUFile *f, uint8_t *buf, int len) +{ + int v; + + v = qemu_get_byte(f); + switch(v) { + case 0: + if (qemu_get_buffer(f, buf, len) != len) + return -EIO; + break; + case 1: + v = qemu_get_byte(f); + memset(buf, v, len); + break; + default: + return -EINVAL; + } + return 0; +} + +static void ram_save(QEMUFile *f, void *opaque) +{ + int i; + qemu_put_be32(f, phys_ram_size); + for(i = 0; i < phys_ram_size; i+= TARGET_PAGE_SIZE) { + ram_put_page(f, phys_ram_base + i, TARGET_PAGE_SIZE); + } +} + +static int ram_load(QEMUFile *f, void *opaque, int version_id) +{ + int i, ret; + + if (version_id != 1) + return -EINVAL; + if (qemu_get_be32(f) != phys_ram_size) + return -EINVAL; + for(i = 0; i < phys_ram_size; i+= TARGET_PAGE_SIZE) { + ret = ram_get_page(f, phys_ram_base + i, TARGET_PAGE_SIZE); + if (ret) + return ret; + } + return 0; +} +#else /* CONFIG_DM */ +void cpu_save(QEMUFile *f, void *opaque) +{ +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + return 0; +} + +static void ram_save(QEMUFile *f, void *opaque) +{ +} + +static int ram_load(QEMUFile *f, void *opaque, int version_id) +{ + return 0; +} +#endif /* CONFIG_DM */ + +/***********************************************************/ +/* machine registration */ + +QEMUMachine *first_machine = NULL; + +int qemu_register_machine(QEMUMachine *m) +{ + QEMUMachine **pm; + pm = &first_machine; + while (*pm != NULL) + pm = &(*pm)->next; + m->next = NULL; + *pm = m; + return 0; +} + +QEMUMachine *find_machine(const char *name) +{ + QEMUMachine *m; + + for(m = first_machine; m != NULL; m = m->next) { + if (!strcmp(m->name, name)) + return m; + } + return NULL; +} + +/***********************************************************/ +/* main execution loop */ + +void gui_update(void *opaque) +{ + display_state.dpy_refresh(&display_state); + qemu_mod_timer(gui_timer, GUI_REFRESH_INTERVAL + qemu_get_clock(rt_clock)); +} + +struct vm_change_state_entry { + VMChangeStateHandler *cb; + void *opaque; + LIST_ENTRY (vm_change_state_entry) entries; +}; + +static LIST_HEAD(vm_change_state_head, vm_change_state_entry) vm_change_state_head; + +VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, + void *opaque) +{ + VMChangeStateEntry *e; + + e = qemu_mallocz(sizeof (*e)); + if (!e) + return NULL; + + e->cb = cb; + e->opaque = opaque; + LIST_INSERT_HEAD(&vm_change_state_head, e, entries); + return e; +} + +void qemu_del_vm_change_state_handler(VMChangeStateEntry *e) +{ + LIST_REMOVE (e, entries); + qemu_free (e); +} + +static void vm_state_notify(int running) +{ + VMChangeStateEntry *e; + + for (e = vm_change_state_head.lh_first; e; e = e->entries.le_next) { + e->cb(e->opaque, running); + } +} + +/* XXX: support several handlers */ +static VMStopHandler *vm_stop_cb; +static void *vm_stop_opaque; + +int qemu_add_vm_stop_handler(VMStopHandler *cb, void *opaque) +{ + vm_stop_cb = cb; + vm_stop_opaque = opaque; + return 0; +} + +void qemu_del_vm_stop_handler(VMStopHandler *cb, void *opaque) +{ + vm_stop_cb = NULL; +} + +void vm_start(void) +{ + if (!vm_running) { + cpu_enable_ticks(); + vm_running = 1; + vm_state_notify(1); + } +} + +void vm_stop(int reason) +{ + if (vm_running) { + cpu_disable_ticks(); + vm_running = 0; + if (reason != 0) { + if (vm_stop_cb) { + vm_stop_cb(vm_stop_opaque, reason); + } + } + vm_state_notify(0); + } +} + +/* reset/shutdown handler */ + +typedef struct QEMUResetEntry { + QEMUResetHandler *func; + void *opaque; + struct QEMUResetEntry *next; +} QEMUResetEntry; + +static QEMUResetEntry *first_reset_entry; +int reset_requested; +int shutdown_requested; +static int powerdown_requested; + +void qemu_register_reset(QEMUResetHandler *func, void *opaque) +{ + QEMUResetEntry **pre, *re; + + pre = &first_reset_entry; + while (*pre != NULL) + pre = &(*pre)->next; + re = qemu_mallocz(sizeof(QEMUResetEntry)); + re->func = func; + re->opaque = opaque; + re->next = NULL; + *pre = re; +} + +void qemu_system_reset(void) +{ + QEMUResetEntry *re; + + /* reset all devices */ + for(re = first_reset_entry; re != NULL; re = re->next) { + re->func(re->opaque); + } +} + +void qemu_system_reset_request(void) +{ + reset_requested = 1; + if (cpu_single_env) + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); +} + +void qemu_system_shutdown_request(void) +{ + shutdown_requested = 1; + if (cpu_single_env) + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); +} + +void qemu_system_powerdown_request(void) +{ + powerdown_requested = 1; + if (cpu_single_env) + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT); +} + +void main_loop_wait(int timeout) +{ + IOHandlerRecord *ioh, *ioh_next; + fd_set rfds, wfds, xfds; + int ret, nfds; + struct timeval tv; + PollingEntry *pe; + + + /* XXX: need to suppress polling by better using win32 events */ + ret = 0; + for(pe = first_polling_entry; pe != NULL; pe = pe->next) { + ret |= pe->func(pe->opaque); + } +#ifdef _WIN32 + if (ret == 0 && timeout > 0) { + int err; + HANDLE hEvents[1]; + + hEvents[0] = host_alarm; + ret = WaitForMultipleObjects(1, hEvents, FALSE, timeout); + switch(ret) { + case WAIT_OBJECT_0 + 0: + break; + case WAIT_TIMEOUT: + break; + default: + err = GetLastError(); + fprintf(stderr, "Wait error %d %d\n", ret, err); + break; + } + } +#endif + /* poll any events */ + /* XXX: separate device handlers from system ones */ + nfds = -1; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&xfds); + for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { + if (ioh->fd_read && + (!ioh->fd_read_poll || + ioh->fd_read_poll(ioh->opaque) != 0)) { + FD_SET(ioh->fd, &rfds); + if (ioh->fd > nfds) + nfds = ioh->fd; + } + if (ioh->fd_write) { + FD_SET(ioh->fd, &wfds); + if (ioh->fd > nfds) + nfds = ioh->fd; + } + } + + tv.tv_sec = 0; +#ifdef _WIN32 + tv.tv_usec = 0; +#else + tv.tv_usec = timeout * 1000; +#endif +#if defined(CONFIG_SLIRP) + if (slirp_inited) { + slirp_select_fill(&nfds, &rfds, &wfds, &xfds); + } +#endif + ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv); + if (ret > 0) { + /* XXX: better handling of removal */ + for(ioh = first_io_handler; ioh != NULL; ioh = ioh_next) { + ioh_next = ioh->next; + if (ioh->fd_read && FD_ISSET(ioh->fd, &rfds)) { + ioh->fd_read(ioh->opaque); + } + if (ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) { + ioh->fd_write(ioh->opaque); + } + } + } +#if defined(CONFIG_SLIRP) + if (slirp_inited) { + if (ret < 0) { + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&xfds); + } + slirp_select_poll(&rfds, &wfds, &xfds); + } +#endif +#ifdef _WIN32 + tap_win32_poll(); +#endif + + if (vm_running) { + qemu_run_timers(&active_timers[QEMU_TIMER_VIRTUAL], + qemu_get_clock(vm_clock)); + /* run dma transfers, if any */ + DMA_run(); + } + + /* real time timers */ + qemu_run_timers(&active_timers[QEMU_TIMER_REALTIME], + qemu_get_clock(rt_clock)); +} + +#ifndef CONFIG_DM +static CPUState *cur_cpu; + +int main_loop(void) +{ + int ret, timeout; +#ifdef CONFIG_PROFILER + int64_t ti; +#endif + CPUState *env; + + cur_cpu = first_cpu; + for(;;) { + if (vm_running) { + + env = cur_cpu; + for(;;) { + /* get next cpu */ + env = env->next_cpu; + if (!env) + env = first_cpu; +#ifdef CONFIG_PROFILER + ti = profile_getclock(); +#endif + ret = cpu_exec(env); +#ifdef CONFIG_PROFILER + qemu_time += profile_getclock() - ti; +#endif + if (ret != EXCP_HALTED) + break; + /* all CPUs are halted ? */ + if (env == cur_cpu) { + ret = EXCP_HLT; + break; + } + } + cur_cpu = env; + + if (shutdown_requested) { + ret = EXCP_INTERRUPT; + break; + } + if (reset_requested) { + reset_requested = 0; + qemu_system_reset(); + ret = EXCP_INTERRUPT; + } + if (powerdown_requested) { + powerdown_requested = 0; + qemu_system_powerdown(); + ret = EXCP_INTERRUPT; + } + if (ret == EXCP_DEBUG) { + vm_stop(EXCP_DEBUG); + } + /* if hlt instruction, we wait until the next IRQ */ + /* XXX: use timeout computed from timers */ + if (ret == EXCP_HLT) + timeout = 10; + else + timeout = 0; + } else { + timeout = 10; + } +#ifdef CONFIG_PROFILER + ti = profile_getclock(); +#endif + main_loop_wait(timeout); +#ifdef CONFIG_PROFILER + dev_time += profile_getclock() - ti; +#endif + } + cpu_disable_ticks(); + return ret; +} +#endif /* !CONFIG_DM */ + +void help(void) +{ + printf("QEMU PC emulator version " QEMU_VERSION ", Copyright (c) 2003-2005 Fabrice Bellard\n" + "usage: %s [options] [disk_image]\n" + "\n" + "'disk_image' is a raw hard image image for IDE hard disk 0\n" + "\n" + "Standard options:\n" + "-M machine select emulated machine (-M ? for list)\n" + "-fda/-fdb file use 'file' as floppy disk 0/1 image\n" + "-hda/-hdb file use 'file' as IDE hard disk 0/1 image\n" + "-hdc/-hdd file use 'file' as IDE hard disk 2/3 image\n" + "-cdrom file use 'file' as IDE cdrom image (cdrom is ide1 master)\n" + "-boot [a|c|d] boot on floppy (a), hard disk (c) or CD-ROM (d)\n" + "-snapshot write to temporary files instead of disk image files\n" + "-m megs set virtual RAM size to megs MB [default=%d]\n" + "-smp n set the number of CPUs to 'n' [default=1]\n" + "-nographic disable graphical output and redirect serial I/Os to console\n" + "-vcpus set CPU number of guest platform\n" +#ifndef _WIN32 + "-k language use keyboard layout (for example \"fr\" for French)\n" +#endif +#ifdef HAS_AUDIO + "-audio-help print list of audio drivers and their options\n" + "-soundhw c1,... enable audio support\n" + " and only specified sound cards (comma separated list)\n" + " use -soundhw ? to get the list of supported cards\n" + " use -soundhw all to enable all of them\n" +#endif + "-localtime set the real time clock to local time [default=utc]\n" + "-full-screen start in full screen\n" +#ifdef TARGET_I386 + "-win2k-hack use it when installing Windows 2000 to avoid a disk full bug\n" +#endif + "-usb enable the USB driver (will be the default soon)\n" + "-usbdevice name add the host or guest USB device 'name'\n" +#if defined(TARGET_PPC) || defined(TARGET_SPARC) + "-g WxH[xDEPTH] Set the initial graphical resolution and depth\n" +#endif + "\n" + "Network options:\n" + "-net nic[,vlan=n][,macaddr=addr][,model=type]\n" + " create a new Network Interface Card and connect it to VLAN 'n'\n" +#ifdef CONFIG_SLIRP + "-net user[,vlan=n][,hostname=host]\n" + " connect the user mode network stack to VLAN 'n' and send\n" + " hostname 'host' to DHCP clients\n" +#endif +#ifdef _WIN32 + "-net tap[,vlan=n],ifname=name\n" + " connect the host TAP network interface to VLAN 'n'\n" +#else + "-net tap[,vlan=n][,fd=h][,ifname=name][,script=file][,bridge=br]\n" + " connect the host TAP network interface to VLAN 'n' and use\n" + " the network script 'file' (default=%s);\n" + " use 'fd=h' to connect to an already opened TAP interface\n" +#endif + "-net socket[,vlan=n][,fd=h][,listen=[host]:port][,connect=host:port]\n" + " connect the vlan 'n' to another VLAN using a socket connection\n" + "-net socket[,vlan=n][,fd=h][,mcast=maddr:port]\n" + " connect the vlan 'n' to multicast maddr and port\n" + "-net none use it alone to have zero network devices; if no -net option\n" + " is provided, the default is '-net nic -net user'\n" + "\n" +#ifdef CONFIG_SLIRP + "-tftp prefix allow tftp access to files starting with prefix [-net user]\n" +#ifndef _WIN32 + "-smb dir allow SMB access to files in 'dir' [-net user]\n" +#endif + "-redir [tcp|udp]:host-port:[guest-host]:guest-port\n" + " redirect TCP or UDP connections from host to guest [-net user]\n" +#endif + "\n" + "Linux boot specific:\n" + "-kernel bzImage use 'bzImage' as kernel image\n" + "-append cmdline use 'cmdline' as kernel command line\n" + "-initrd file use 'file' as initial ram disk\n" + "\n" + "Debug/Expert options:\n" + "-monitor dev redirect the monitor to char device 'dev'\n" + "-serial dev redirect the serial port to char device 'dev'\n" + "-parallel dev redirect the parallel port to char device 'dev'\n" + "-pidfile file Write PID to 'file'\n" + "-S freeze CPU at startup (use 'c' to start execution)\n" + "-s wait gdb connection to port %d\n" + "-p port change gdb connection port\n" + "-l item1,... output log to %s (use -d ? for a list of log items)\n" + "-d domain domain that we're serving\n" + "-domain-name domain name that we're serving\n" + "-hdachs c,h,s[,t] force hard disk 0 physical geometry and the optional BIOS\n" + " translation (t=none or lba) (usually qemu can guess them)\n" + "-L path set the directory for the BIOS and VGA BIOS\n" +#ifdef USE_KQEMU + "-no-kqemu disable KQEMU kernel module usage\n" +#endif +#ifdef USE_CODE_COPY + "-no-code-copy disable code copy acceleration\n" +#endif +#ifdef TARGET_I386 + "-std-vga simulate a standard VGA card with VESA Bochs Extensions\n" + " (default is CL-GD5446 PCI VGA)\n" +#endif + "-loadvm file start right away with a saved state (loadvm in monitor)\n" + "-vnc display start a VNC server on display\n" + "-timeoffset time offset (in seconds) from local time\n" + "\n" + "During emulation, the following keys are useful:\n" + "ctrl-alt-f toggle full screen\n" + "ctrl-alt-n switch to virtual console 'n'\n" + "ctrl-alt toggle mouse and keyboard grab\n" + "\n" + "When using -nographic, press 'ctrl-a h' to get some help.\n" + , +#ifdef CONFIG_SOFTMMU + "qemu", +#else + "qemu-fast", +#endif + DEFAULT_RAM_SIZE, +#ifndef _WIN32 + DEFAULT_NETWORK_SCRIPT, +#endif + DEFAULT_GDBSTUB_PORT, + "/tmp/qemu.log"); +#ifndef CONFIG_SOFTMMU + printf("\n" + "NOTE: this version of QEMU is faster but it needs slightly patched OSes to\n" + "work. Please use the 'qemu' executable to have a more accurate (but slower)\n" + "PC emulation.\n"); +#endif + exit(1); +} + +#define HAS_ARG 0x0001 + +enum { + QEMU_OPTION_h, + + QEMU_OPTION_M, + QEMU_OPTION_fda, + QEMU_OPTION_fdb, + QEMU_OPTION_hda, + QEMU_OPTION_hdb, + QEMU_OPTION_hdc, + QEMU_OPTION_hdd, + QEMU_OPTION_cdrom, + QEMU_OPTION_boot, + QEMU_OPTION_snapshot, + QEMU_OPTION_m, + QEMU_OPTION_nographic, +#ifdef HAS_AUDIO + QEMU_OPTION_audio_help, + QEMU_OPTION_soundhw, +#endif + + QEMU_OPTION_net, + QEMU_OPTION_tftp, + QEMU_OPTION_smb, + QEMU_OPTION_redir, + + QEMU_OPTION_kernel, + QEMU_OPTION_append, + QEMU_OPTION_initrd, + + QEMU_OPTION_S, + QEMU_OPTION_s, + QEMU_OPTION_p, + QEMU_OPTION_l, + QEMU_OPTION_hdachs, + QEMU_OPTION_L, +#ifdef USE_CODE_COPY + QEMU_OPTION_no_code_copy, +#endif + QEMU_OPTION_k, + QEMU_OPTION_localtime, + QEMU_OPTION_cirrusvga, + QEMU_OPTION_g, + QEMU_OPTION_std_vga, + QEMU_OPTION_monitor, + QEMU_OPTION_domainname, + QEMU_OPTION_serial, + QEMU_OPTION_parallel, + QEMU_OPTION_loadvm, + QEMU_OPTION_full_screen, + QEMU_OPTION_pidfile, + QEMU_OPTION_no_kqemu, + QEMU_OPTION_kernel_kqemu, + QEMU_OPTION_win2k_hack, + QEMU_OPTION_usb, + QEMU_OPTION_usbdevice, + QEMU_OPTION_smp, + QEMU_OPTION_vnc, + + QEMU_OPTION_d, + QEMU_OPTION_vcpus, + QEMU_OPTION_timeoffset, +}; + +typedef struct QEMUOption { + const char *name; + int flags; + int index; +} QEMUOption; + +const QEMUOption qemu_options[] = { + { "h", 0, QEMU_OPTION_h }, + + { "M", HAS_ARG, QEMU_OPTION_M }, + { "fda", HAS_ARG, QEMU_OPTION_fda }, + { "fdb", HAS_ARG, QEMU_OPTION_fdb }, + { "hda", HAS_ARG, QEMU_OPTION_hda }, + { "hdb", HAS_ARG, QEMU_OPTION_hdb }, + { "hdc", HAS_ARG, QEMU_OPTION_hdc }, + { "hdd", HAS_ARG, QEMU_OPTION_hdd }, + { "cdrom", HAS_ARG, QEMU_OPTION_cdrom }, + { "boot", HAS_ARG, QEMU_OPTION_boot }, + { "snapshot", 0, QEMU_OPTION_snapshot }, + { "m", HAS_ARG, QEMU_OPTION_m }, + { "nographic", 0, QEMU_OPTION_nographic }, + { "k", HAS_ARG, QEMU_OPTION_k }, +#ifdef HAS_AUDIO + { "audio-help", 0, QEMU_OPTION_audio_help }, + { "soundhw", HAS_ARG, QEMU_OPTION_soundhw }, +#endif + + { "net", HAS_ARG, QEMU_OPTION_net}, +#ifdef CONFIG_SLIRP + { "tftp", HAS_ARG, QEMU_OPTION_tftp }, +#ifndef _WIN32 + { "smb", HAS_ARG, QEMU_OPTION_smb }, +#endif + { "redir", HAS_ARG, QEMU_OPTION_redir }, +#endif + + { "kernel", HAS_ARG, QEMU_OPTION_kernel }, + { "append", HAS_ARG, QEMU_OPTION_append }, + { "initrd", HAS_ARG, QEMU_OPTION_initrd }, + + { "S", 0, QEMU_OPTION_S }, + { "s", 0, QEMU_OPTION_s }, + { "p", HAS_ARG, QEMU_OPTION_p }, + { "l", HAS_ARG, QEMU_OPTION_l }, + { "hdachs", HAS_ARG, QEMU_OPTION_hdachs }, + { "L", HAS_ARG, QEMU_OPTION_L }, +#ifdef USE_CODE_COPY + { "no-code-copy", 0, QEMU_OPTION_no_code_copy }, +#endif +#ifdef USE_KQEMU + { "no-kqemu", 0, QEMU_OPTION_no_kqemu }, + { "kernel-kqemu", 0, QEMU_OPTION_kernel_kqemu }, +#endif +#if defined(TARGET_PPC) || defined(TARGET_SPARC) + { "g", 1, QEMU_OPTION_g }, +#endif + { "localtime", 0, QEMU_OPTION_localtime }, + { "std-vga", 0, QEMU_OPTION_std_vga }, + { "monitor", 1, QEMU_OPTION_monitor }, + { "domain-name", 1, QEMU_OPTION_domainname }, + { "serial", 1, QEMU_OPTION_serial }, + { "parallel", 1, QEMU_OPTION_parallel }, + { "loadvm", HAS_ARG, QEMU_OPTION_loadvm }, + { "full-screen", 0, QEMU_OPTION_full_screen }, + { "pidfile", HAS_ARG, QEMU_OPTION_pidfile }, + { "win2k-hack", 0, QEMU_OPTION_win2k_hack }, + { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice }, + { "smp", HAS_ARG, QEMU_OPTION_smp }, + { "vnc", HAS_ARG, QEMU_OPTION_vnc }, + + /* temporary options */ + { "usb", 0, QEMU_OPTION_usb }, + { "cirrusvga", 0, QEMU_OPTION_cirrusvga }, + + { "d", HAS_ARG, QEMU_OPTION_d }, + { "vcpus", 1, QEMU_OPTION_vcpus }, + { "timeoffset", HAS_ARG, QEMU_OPTION_timeoffset }, + { NULL }, +}; + +#if defined (TARGET_I386) && defined(USE_CODE_COPY) + +/* this stack is only used during signal handling */ +#define SIGNAL_STACK_SIZE 32768 + +static uint8_t *signal_stack; + +#endif + +/* password input */ + +static BlockDriverState *get_bdrv(int index) +{ + BlockDriverState *bs; + + if (index < 4) { + bs = bs_table[index]; + } else if (index < 6) { + bs = fd_table[index - 4]; + } else { + bs = NULL; + } + return bs; +} + +static void read_passwords(void) +{ + BlockDriverState *bs; + int i, j; + char password[256]; + + for(i = 0; i < 6; i++) { + bs = get_bdrv(i); + if (bs && bdrv_is_encrypted(bs)) { + term_printf("%s is encrypted.\n", bdrv_get_device_name(bs)); + for(j = 0; j < 3; j++) { + monitor_readline("Password: ", + 1, password, sizeof(password)); + if (bdrv_set_key(bs, password) == 0) + break; + term_printf("invalid password\n"); + } + } + } +} + +/* XXX: currently we cannot use simultaneously different CPUs */ +void register_machines(void) +{ +#if defined(TARGET_I386) + qemu_register_machine(&pc_machine); + qemu_register_machine(&isapc_machine); +#elif defined(TARGET_PPC) + qemu_register_machine(&heathrow_machine); + qemu_register_machine(&core99_machine); + qemu_register_machine(&prep_machine); +#elif defined(TARGET_MIPS) + qemu_register_machine(&mips_machine); +#elif defined(TARGET_SPARC) +#ifdef TARGET_SPARC64 + qemu_register_machine(&sun4u_machine); +#else + qemu_register_machine(&sun4m_machine); +#endif +#elif defined(TARGET_ARM) + qemu_register_machine(&integratorcp926_machine); + qemu_register_machine(&integratorcp1026_machine); + qemu_register_machine(&versatilepb_machine); + qemu_register_machine(&versatileab_machine); +#elif defined(TARGET_SH4) + qemu_register_machine(&shix_machine); +#else +#error unsupported CPU +#endif +} + +#ifdef HAS_AUDIO +struct soundhw soundhw[] = { +#ifndef CONFIG_DM +#ifdef TARGET_I386 + { + "pcspk", + "PC speaker", + 0, + 1, + { .init_isa = pcspk_audio_init } + }, +#endif +#endif /* !CONFIG_DM */ + { + "sb16", + "Creative Sound Blaster 16", + 0, + 1, + { .init_isa = SB16_init } + }, + +#ifdef CONFIG_ADLIB + { + "adlib", +#ifdef HAS_YMF262 + "Yamaha YMF262 (OPL3)", +#else + "Yamaha YM3812 (OPL2)", +#endif + 0, + 1, + { .init_isa = Adlib_init } + }, +#endif + +#ifdef CONFIG_GUS + { + "gus", + "Gravis Ultrasound GF1", + 0, + 1, + { .init_isa = GUS_init } + }, +#endif + + { + "es1370", + "ENSONIQ AudioPCI ES1370", + 0, + 0, + { .init_pci = es1370_init } + }, + + { NULL, NULL, 0, 0, { NULL } } +}; + +static void select_soundhw (const char *optarg) +{ + struct soundhw *c; + + if (*optarg == '?') { + show_valid_cards: + + printf ("Valid sound card names (comma separated):\n"); + for (c = soundhw; c->name; ++c) { + printf ("%-11s %s\n", c->name, c->descr); + } + printf ("\n-soundhw all will enable all of the above\n"); + exit (*optarg != '?'); + } + else { + size_t l; + const char *p; + char *e; + int bad_card = 0; + + if (!strcmp (optarg, "all")) { + for (c = soundhw; c->name; ++c) { + c->enabled = 1; + } + return; + } + + p = optarg; + while (*p) { + e = strchr (p, ','); + l = !e ? strlen (p) : (size_t) (e - p); + + for (c = soundhw; c->name; ++c) { + if (!strncmp (c->name, p, l)) { + c->enabled = 1; + break; + } + } + + if (!c->name) { + if (l > 80) { + fprintf (stderr, + "Unknown sound card name (too big to show)\n"); + } + else { + fprintf (stderr, "Unknown sound card name `%.*s'\n", + (int) l, p); + } + bad_card = 1; + } + p += l + (e != NULL); + } + + if (bad_card) + goto show_valid_cards; + } +} +#endif + +#define MAX_NET_CLIENTS 32 + +#include + +/* FIXME Flush the shadow page */ +int unset_mm_mapping(int xc_handle, uint32_t domid, + unsigned long nr_pages, unsigned int address_bits, + xen_pfn_t *extent_start) +{ + int err = 0; + xc_dominfo_t info; + + err = xc_domain_memory_decrease_reservation(xc_handle, domid, + nr_pages, 0, extent_start); + if (err) + fprintf(stderr, "Failed to decrease physmap\n"); + + xc_domain_getinfo(xc_handle, domid, 1, &info); + + if ((info.nr_pages - nr_pages) <= 0) { + fprintf(stderr, "unset_mm_mapping: error nr_pages\n"); + err = -1; + } + + if (xc_domain_setmaxmem(xc_handle, domid, (info.nr_pages - nr_pages) * + PAGE_SIZE/1024) != 0) { + fprintf(logfile, "set maxmem returned error %d\n", errno); + err = -1; + } + + return err; +} + +int set_mm_mapping(int xc_handle, uint32_t domid, + unsigned long nr_pages, unsigned int address_bits, + xen_pfn_t *extent_start) +{ +#if 0 + int i; +#endif + xc_dominfo_t info; + int err = 0; + + xc_domain_getinfo(xc_handle, domid, 1, &info); + + if (xc_domain_setmaxmem(xc_handle, domid, info.max_memkb + + nr_pages * PAGE_SIZE/1024) != 0) { + fprintf(logfile, "set maxmem returned error %d\n", errno); + return -1; + } + + err = xc_domain_memory_populate_physmap(xc_handle, domid, nr_pages, 0, + address_bits, extent_start); + if (err) { + fprintf(stderr, "Failed to populate physmap\n"); + return -1; + } + + err = xc_domain_translate_gpfn_list(xc_handle, domid, nr_pages, + extent_start, extent_start); + if (err) { + fprintf(stderr, "Failed to translate gpfn list\n"); + return -1; + } + +#if 0 /* Generates lots of log file output - turn on for debugging */ + for (i = 0; i < nr_pages; i++) + fprintf(stderr, "set_map result i %x result %lx\n", i, + extent_start[i]); +#endif + + return 0; +} + +int main(int argc, char **argv) +{ +#ifdef CONFIG_GDBSTUB + int use_gdbstub, gdbstub_port; +#endif + int i, cdrom_index; + int snapshot, linux_boot; + const char *initrd_filename; + const char *hd_filename[MAX_DISKS], *fd_filename[MAX_FD]; + const char *kernel_filename, *kernel_cmdline; + DisplayState *ds = &display_state; + int cyls, heads, secs, translation; + int start_emulation = 1; + char net_clients[MAX_NET_CLIENTS][256]; + int nb_net_clients; + int optind; + const char *r, *optarg; + CharDriverState *monitor_hd; + char monitor_device[128]; + char serial_devices[MAX_SERIAL_PORTS][128]; + int serial_device_index; + char parallel_devices[MAX_PARALLEL_PORTS][128]; + int parallel_device_index; + const char *loadvm = NULL; + QEMUMachine *machine; + char usb_devices[MAX_VM_USB_PORTS][128]; + int usb_devices_index; + unsigned long nr_pages; + xen_pfn_t *page_array; + extern void *shared_page; + + char qemu_dm_logfilename[64]; + + LIST_INIT (&vm_change_state_head); +#if !defined(CONFIG_SOFTMMU) + /* we never want that malloc() uses mmap() */ + mallopt(M_MMAP_THRESHOLD, 4096 * 1024); +#endif + register_machines(); + machine = first_machine; + initrd_filename = NULL; + for(i = 0; i < MAX_FD; i++) + fd_filename[i] = NULL; + for(i = 0; i < MAX_DISKS; i++) + hd_filename[i] = NULL; + ram_size = DEFAULT_RAM_SIZE * 1024 * 1024; + vga_ram_size = VGA_RAM_SIZE; + bios_size = BIOS_SIZE; +#ifdef CONFIG_GDBSTUB + use_gdbstub = 0; + gdbstub_port = DEFAULT_GDBSTUB_PORT; +#endif + snapshot = 0; + nographic = 0; + kernel_filename = NULL; + kernel_cmdline = ""; +#ifdef TARGET_PPC + cdrom_index = 1; +#else + cdrom_index = 2; +#endif + cyls = heads = secs = 0; + translation = BIOS_ATA_TRANSLATION_AUTO; + pstrcpy(monitor_device, sizeof(monitor_device), "vc"); + + pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "vc"); + for(i = 1; i < MAX_SERIAL_PORTS; i++) + serial_devices[i][0] = '\0'; + serial_device_index = 0; + + pstrcpy(parallel_devices[0], sizeof(parallel_devices[0]), "vc"); + for(i = 1; i < MAX_PARALLEL_PORTS; i++) + parallel_devices[i][0] = '\0'; + parallel_device_index = 0; + + usb_devices_index = 0; + + nb_net_clients = 0; + + nb_nics = 0; + /* default mac address of the first network interface */ + + /* init debug */ + sprintf(qemu_dm_logfilename, "/var/log/qemu-dm.%d.log", getpid()); + cpu_set_log_filename(qemu_dm_logfilename); + cpu_set_log(0); + + optind = 1; + for(;;) { + if (optind >= argc) + break; + r = argv[optind]; + if (r[0] != '-') { + hd_filename[0] = argv[optind++]; + } else { + const QEMUOption *popt; + + optind++; + popt = qemu_options; + for(;;) { + if (!popt->name) { + fprintf(stderr, "%s: invalid option -- '%s'\n", + argv[0], r); + exit(1); + } + if (!strcmp(popt->name, r + 1)) + break; + popt++; + } + if (popt->flags & HAS_ARG) { + if (optind >= argc) { + fprintf(stderr, "%s: option '%s' requires an argument\n", + argv[0], r); + exit(1); + } + optarg = argv[optind++]; + } else { + optarg = NULL; + } + + switch(popt->index) { + case QEMU_OPTION_M: + machine = find_machine(optarg); + if (!machine) { + QEMUMachine *m; + printf("Supported machines are:\n"); + for(m = first_machine; m != NULL; m = m->next) { + printf("%-10s %s%s\n", + m->name, m->desc, + m == first_machine ? " (default)" : ""); + } + exit(1); + } + break; + case QEMU_OPTION_initrd: + initrd_filename = optarg; + break; + case QEMU_OPTION_hda: + case QEMU_OPTION_hdb: + case QEMU_OPTION_hdc: + case QEMU_OPTION_hdd: + { + int hd_index; + hd_index = popt->index - QEMU_OPTION_hda; + hd_filename[hd_index] = optarg; + if (hd_index == cdrom_index) + cdrom_index = -1; + } + break; + case QEMU_OPTION_snapshot: + snapshot = 1; + break; + case QEMU_OPTION_hdachs: + { + const char *p; + p = optarg; + cyls = strtol(p, (char **)&p, 0); + if (cyls < 1 || cyls > 16383) + goto chs_fail; + if (*p != ',') + goto chs_fail; + p++; + heads = strtol(p, (char **)&p, 0); + if (heads < 1 || heads > 16) + goto chs_fail; + if (*p != ',') + goto chs_fail; + p++; + secs = strtol(p, (char **)&p, 0); + if (secs < 1 || secs > 63) + goto chs_fail; + if (*p == ',') { + p++; + if (!strcmp(p, "none")) + translation = BIOS_ATA_TRANSLATION_NONE; + else if (!strcmp(p, "lba")) + translation = BIOS_ATA_TRANSLATION_LBA; + else if (!strcmp(p, "auto")) + translation = BIOS_ATA_TRANSLATION_AUTO; + else + goto chs_fail; + } else if (*p != '\0') { + chs_fail: + fprintf(stderr, "qemu: invalid physical CHS format\n"); + exit(1); + } + } + break; + case QEMU_OPTION_nographic: + pstrcpy(monitor_device, sizeof(monitor_device), "stdio"); + if(!strcmp(serial_devices[0], "vc")) + pstrcpy(serial_devices[0], sizeof(serial_devices[0]), + "stdio"); + nographic = 1; + break; + case QEMU_OPTION_kernel: + kernel_filename = optarg; + break; + case QEMU_OPTION_append: + kernel_cmdline = optarg; + break; + case QEMU_OPTION_cdrom: + if (cdrom_index >= 0) { + hd_filename[cdrom_index] = optarg; + } + break; + case QEMU_OPTION_boot: + boot_device = optarg[0]; + if (boot_device != 'a' && +#ifdef TARGET_SPARC + // Network boot + boot_device != 'n' && +#endif + boot_device != 'c' && boot_device != 'd') { + fprintf(stderr, "qemu: invalid boot device '%c'\n", boot_device); + exit(1); + } + break; + case QEMU_OPTION_fda: + fd_filename[0] = optarg; + break; + case QEMU_OPTION_fdb: + fd_filename[1] = optarg; + break; +#ifdef USE_CODE_COPY + case QEMU_OPTION_no_code_copy: + code_copy_enabled = 0; + break; +#endif + case QEMU_OPTION_net: + if (nb_net_clients >= MAX_NET_CLIENTS) { + fprintf(stderr, "qemu: too many network clients\n"); + exit(1); + } + pstrcpy(net_clients[nb_net_clients], + sizeof(net_clients[0]), + optarg); + nb_net_clients++; + break; +#ifdef CONFIG_SLIRP + case QEMU_OPTION_tftp: + tftp_prefix = optarg; + break; +#ifndef _WIN32 + case QEMU_OPTION_smb: + net_slirp_smb(optarg); + break; +#endif + case QEMU_OPTION_redir: + net_slirp_redir(optarg); + break; +#endif +#ifdef HAS_AUDIO + case QEMU_OPTION_audio_help: + AUD_help (); + exit (0); + break; + case QEMU_OPTION_soundhw: + select_soundhw (optarg); + break; +#endif + case QEMU_OPTION_h: + help(); + break; + case QEMU_OPTION_m: + ram_size = atol(optarg) * 1024 * 1024; + if (ram_size <= 0) + help(); +#ifndef CONFIG_DM + if (ram_size > PHYS_RAM_MAX_SIZE) { + fprintf(stderr, "qemu: at most %d MB RAM can be simulated\n", + PHYS_RAM_MAX_SIZE / (1024 * 1024)); + exit(1); + } +#endif /* !CONFIG_DM */ + break; + case QEMU_OPTION_l: + { + int mask; + CPULogItem *item; + + mask = cpu_str_to_log_mask(optarg); + if (!mask) { + printf("Log items (comma separated):\n"); + for(item = cpu_log_items; item->mask != 0; item++) { + printf("%-10s %s\n", item->name, item->help); + } + exit(1); + } + cpu_set_log(mask); + } + break; +#ifdef CONFIG_GDBSTUB + case QEMU_OPTION_s: + use_gdbstub = 1; + break; + case QEMU_OPTION_p: + gdbstub_port = atoi(optarg); + break; +#endif + case QEMU_OPTION_L: + bios_dir = optarg; + break; + case QEMU_OPTION_S: + start_emulation = 0; + break; + case QEMU_OPTION_k: + keyboard_layout = optarg; + break; + case QEMU_OPTION_localtime: + rtc_utc = 0; + break; + case QEMU_OPTION_cirrusvga: + cirrus_vga_enabled = 1; + break; + case QEMU_OPTION_std_vga: + cirrus_vga_enabled = 0; + break; + case QEMU_OPTION_g: + { + const char *p; + int w, h, depth; + p = optarg; + w = strtol(p, (char **)&p, 10); + if (w <= 0) { + graphic_error: + fprintf(stderr, "qemu: invalid resolution or depth\n"); + exit(1); + } + if (*p != 'x') + goto graphic_error; + p++; + h = strtol(p, (char **)&p, 10); + if (h <= 0) + goto graphic_error; + if (*p == 'x') { + p++; + depth = strtol(p, (char **)&p, 10); + if (depth != 8 && depth != 15 && depth != 16 && + depth != 24 && depth != 32) + goto graphic_error; + } else if (*p == '\0') { + depth = graphic_depth; + } else { + goto graphic_error; + } + + graphic_width = w; + graphic_height = h; + graphic_depth = depth; + } + break; + case QEMU_OPTION_monitor: + pstrcpy(monitor_device, sizeof(monitor_device), optarg); + break; + case QEMU_OPTION_serial: + if (serial_device_index >= MAX_SERIAL_PORTS) { + fprintf(stderr, "qemu: too many serial ports\n"); + exit(1); + } + pstrcpy(serial_devices[serial_device_index], + sizeof(serial_devices[0]), optarg); + serial_device_index++; + break; + case QEMU_OPTION_parallel: + if (parallel_device_index >= MAX_PARALLEL_PORTS) { + fprintf(stderr, "qemu: too many parallel ports\n"); + exit(1); + } + pstrcpy(parallel_devices[parallel_device_index], + sizeof(parallel_devices[0]), optarg); + parallel_device_index++; + break; + case QEMU_OPTION_loadvm: + loadvm = optarg; + break; + case QEMU_OPTION_full_screen: + full_screen = 1; + break; + case QEMU_OPTION_pidfile: + create_pidfile(optarg); + break; +#ifdef TARGET_I386 + case QEMU_OPTION_win2k_hack: + win2k_install_hack = 1; + break; +#endif +#ifdef USE_KQEMU + case QEMU_OPTION_no_kqemu: + kqemu_allowed = 0; + break; + case QEMU_OPTION_kernel_kqemu: + kqemu_allowed = 2; + break; +#endif + case QEMU_OPTION_usb: + usb_enabled = 1; + break; + case QEMU_OPTION_usbdevice: + usb_enabled = 1; + if (usb_devices_index >= MAX_VM_USB_PORTS) { + fprintf(stderr, "Too many USB devices\n"); + exit(1); + } + pstrcpy(usb_devices[usb_devices_index], + sizeof(usb_devices[usb_devices_index]), + optarg); + usb_devices_index++; + break; + case QEMU_OPTION_smp: + smp_cpus = atoi(optarg); + if (smp_cpus < 1 || smp_cpus > MAX_CPUS) { + fprintf(stderr, "Invalid number of CPUs\n"); + exit(1); + } + break; + case QEMU_OPTION_vnc: + vnc_display = atoi(optarg); + if (vnc_display < 0) { + fprintf(stderr, "Invalid VNC display\n"); + exit(1); + } + break; + case QEMU_OPTION_domainname: + strncat(domain_name, optarg, sizeof(domain_name) - 20); + break; + case QEMU_OPTION_d: + domid = atoi(optarg); + fprintf(logfile, "domid: %d\n", domid); + break; + case QEMU_OPTION_vcpus: + vcpus = atoi(optarg); + fprintf(logfile, "qemu: the number of cpus is %d\n", vcpus); + break; + case QEMU_OPTION_timeoffset: + timeoffset = strtol(optarg, NULL, 0); + break; + } + } + } + +#ifdef USE_KQEMU + if (smp_cpus > 1) + kqemu_allowed = 0; +#endif + linux_boot = (kernel_filename != NULL); + + if (!linux_boot && + hd_filename[0] == '\0' && + (cdrom_index >= 0 && hd_filename[cdrom_index] == '\0') && + fd_filename[0] == '\0') + help(); + + /* boot to cd by default if no hard disk */ + if (hd_filename[0] == '\0' && boot_device == 'c') { + if (fd_filename[0] != '\0') + boot_device = 'a'; + else + boot_device = 'd'; + } + +#if !defined(CONFIG_SOFTMMU) + /* must avoid mmap() usage of glibc by setting a buffer "by hand" */ + { + static uint8_t stdout_buf[4096]; + setvbuf(stdout, stdout_buf, _IOLBF, sizeof(stdout_buf)); + } +#else + setvbuf(stdout, NULL, _IOLBF, 0); +#endif + +#ifdef _WIN32 + socket_init(); +#endif + + /* init network clients */ + if (nb_net_clients == 0) { + /* if no clients, we use a default config */ + pstrcpy(net_clients[0], sizeof(net_clients[0]), + "nic"); + pstrcpy(net_clients[1], sizeof(net_clients[0]), + "user"); + nb_net_clients = 2; + } + + for(i = 0;i < nb_net_clients; i++) { + if (net_client_init(net_clients[i]) < 0) + exit(1); + } + + /* init the memory */ + phys_ram_size = ram_size + vga_ram_size + bios_size; + +#ifdef CONFIG_DM + + nr_pages = ram_size/PAGE_SIZE; + xc_handle = xc_interface_open(); + + page_array = (xen_pfn_t *)malloc(nr_pages * sizeof(xen_pfn_t)); + if (page_array == NULL) { + fprintf(logfile, "malloc returned error %d\n", errno); + exit(-1); + } + + if (xc_get_pfn_list(xc_handle, domid, page_array, nr_pages) != nr_pages) { + fprintf(logfile, "xc_get_pfn_list returned error %d\n", errno); + exit(-1); + } + + phys_ram_base = xc_map_foreign_batch(xc_handle, domid, + PROT_READ|PROT_WRITE, page_array, + nr_pages - 1); + if (phys_ram_base == 0) { + fprintf(logfile, "xc_map_foreign_batch returned error %d\n", errno); + exit(-1); + } + + shared_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, + PROT_READ|PROT_WRITE, + page_array[nr_pages - 1]); + + fprintf(logfile, "shared page at pfn:%lx, mfn: %"PRIx64"\n", nr_pages - 1, + (uint64_t)(page_array[nr_pages - 1])); + +#else /* !CONFIG_DM */ + +#ifdef CONFIG_SOFTMMU + phys_ram_base = qemu_vmalloc(phys_ram_size); + if (!phys_ram_base) { + fprintf(stderr, "Could not allocate physical memory\n"); + exit(1); + } +#else + /* as we must map the same page at several addresses, we must use + a fd */ + { + const char *tmpdir; + + tmpdir = getenv("QEMU_TMPDIR"); + if (!tmpdir) + tmpdir = "/tmp"; + snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/vlXXXXXX", tmpdir); + if (mkstemp(phys_ram_file) < 0) { + fprintf(stderr, "Could not create temporary memory file '%s'\n", + phys_ram_file); + exit(1); + } + phys_ram_fd = open(phys_ram_file, O_CREAT | O_TRUNC | O_RDWR, 0600); + if (phys_ram_fd < 0) { + fprintf(stderr, "Could not open temporary memory file '%s'\n", + phys_ram_file); + exit(1); + } + ftruncate(phys_ram_fd, phys_ram_size); + unlink(phys_ram_file); + phys_ram_base = mmap(get_mmap_addr(phys_ram_size), + phys_ram_size, + PROT_WRITE | PROT_READ, MAP_SHARED | MAP_FIXED, + phys_ram_fd, 0); + if (phys_ram_base == MAP_FAILED) { + fprintf(stderr, "Could not map physical memory\n"); + exit(1); + } + } +#endif + +#endif /* !CONFIG_DM */ + + /* we always create the cdrom drive, even if no disk is there */ + bdrv_init(); + if (cdrom_index >= 0) { + bs_table[cdrom_index] = bdrv_new("cdrom"); + bdrv_set_type_hint(bs_table[cdrom_index], BDRV_TYPE_CDROM); + } + + /* open the virtual block devices */ + for(i = 0; i < MAX_DISKS; i++) { + if (hd_filename[i]) { + if (!bs_table[i]) { + char buf[64]; + snprintf(buf, sizeof(buf), "hd%c", i + 'a'); + bs_table[i] = bdrv_new(buf); + } + if (bdrv_open(bs_table[i], hd_filename[i], snapshot) < 0) { + fprintf(stderr, "qemu: could not open hard disk image '%s'\n", + hd_filename[i]); + exit(1); + } + if (i == 0 && cyls != 0) { + bdrv_set_geometry_hint(bs_table[i], cyls, heads, secs); + bdrv_set_translation_hint(bs_table[i], translation); + } + } + } + + /* we always create at least one floppy disk */ + fd_table[0] = bdrv_new("fda"); + bdrv_set_type_hint(fd_table[0], BDRV_TYPE_FLOPPY); + + for(i = 0; i < MAX_FD; i++) { + if (fd_filename[i]) { + if (!fd_table[i]) { + char buf[64]; + snprintf(buf, sizeof(buf), "fd%c", i + 'a'); + fd_table[i] = bdrv_new(buf); + bdrv_set_type_hint(fd_table[i], BDRV_TYPE_FLOPPY); + } + if (fd_filename[i] != '\0') { + if (bdrv_open(fd_table[i], fd_filename[i], snapshot) < 0) { + fprintf(stderr, "qemu: could not open floppy disk image '%s'\n", + fd_filename[i]); + exit(1); + } + } + } + } + + /* init USB devices */ + if (usb_enabled) { + vm_usb_hub = usb_hub_init(vm_usb_ports, MAX_VM_USB_PORTS); + for(i = 0; i < usb_devices_index; i++) { + if (usb_device_add(usb_devices[i]) < 0) { + fprintf(stderr, "Warning: could not add USB device %s\n", + usb_devices[i]); + } + } + } + + register_savevm("timer", 0, 1, timer_save, timer_load, NULL); + register_savevm("ram", 0, 1, ram_save, ram_load, NULL); + + init_ioports(); + cpu_calibrate_ticks(); + + /* terminal init */ + if (nographic) { + dumb_display_init(ds); + } else if (vnc_display != -1) { + vnc_display_init(ds, vnc_display); + } else { +#if defined(CONFIG_SDL) + sdl_display_init(ds, full_screen); +#elif defined(CONFIG_COCOA) + cocoa_display_init(ds, full_screen); +#else + dumb_display_init(ds); +#endif + } + + monitor_hd = qemu_chr_open(monitor_device); + if (!monitor_hd) { + fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device); + exit(1); + } + monitor_init(monitor_hd, !nographic); + + for(i = 0; i < MAX_SERIAL_PORTS; i++) { + if (serial_devices[i][0] != '\0') { + serial_hds[i] = qemu_chr_open(serial_devices[i]); + if (!serial_hds[i]) { + fprintf(stderr, "qemu: could not open serial device '%s'\n", + serial_devices[i]); + exit(1); + } + if (!strcmp(serial_devices[i], "vc")) + qemu_chr_printf(serial_hds[i], "serial%d console\n", i); + } + } + + for(i = 0; i < MAX_PARALLEL_PORTS; i++) { + if (parallel_devices[i][0] != '\0') { + parallel_hds[i] = qemu_chr_open(parallel_devices[i]); + if (!parallel_hds[i]) { + fprintf(stderr, "qemu: could not open parallel device '%s'\n", + parallel_devices[i]); + exit(1); + } + if (!strcmp(parallel_devices[i], "vc")) + qemu_chr_printf(parallel_hds[i], "parallel%d console\n", i); + } + } + + /* setup cpu signal handlers for MMU / self modifying code handling */ +#if !defined(CONFIG_SOFTMMU) + +#if defined (TARGET_I386) && defined(USE_CODE_COPY) + { + stack_t stk; + signal_stack = memalign(16, SIGNAL_STACK_SIZE); + stk.ss_sp = signal_stack; + stk.ss_size = SIGNAL_STACK_SIZE; + stk.ss_flags = 0; + + if (sigaltstack(&stk, NULL) < 0) { + fprintf(logfile, "sigaltstack returned error %d\n", errno); + exit(1); + } + } +#endif + { + struct sigaction act; + + sigfillset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; +#if defined (TARGET_I386) && defined(USE_CODE_COPY) + act.sa_flags |= SA_ONSTACK; +#endif + act.sa_sigaction = host_segv_handler; + sigaction(SIGSEGV, &act, NULL); + sigaction(SIGBUS, &act, NULL); +#if defined (TARGET_I386) && defined(USE_CODE_COPY) + sigaction(SIGFPE, &act, NULL); +#endif + } +#endif + +#ifndef _WIN32 + { + struct sigaction act; + sigfillset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); + } +#endif + init_timers(); + + machine->init(ram_size, vga_ram_size, boot_device, + ds, fd_filename, snapshot, + kernel_filename, kernel_cmdline, initrd_filename, + timeoffset); + + display_state.dpy_refresh(&display_state); + +#ifdef CONFIG_GDBSTUB + if (use_gdbstub) { + if (gdbserver_start(gdbstub_port) < 0) { + fprintf(stderr, "Could not open gdbserver socket on port %d\n", + gdbstub_port); + exit(1); + } else { + printf("Waiting gdb connection on port %d\n", gdbstub_port); + } + } else +#endif + if (loadvm) + qemu_loadvm(loadvm); + + { + /* XXX: simplify init */ + read_passwords(); + if (start_emulation) { + vm_start(); + } + } + main_loop(); + quit_timers(); + return 0; +} diff --git a/tools/ioemu/vl.h b/tools/ioemu/vl.h new file mode 100644 index 0000000000..c16a1b64f4 --- /dev/null +++ b/tools/ioemu/vl.h @@ -0,0 +1,1111 @@ +/* + * QEMU System Emulator header + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef VL_H +#define VL_H + +/* we put basic includes here to avoid repeating them in device drivers */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audio/audio.h" +#include "xenctrl.h" +#include "xs.h" + +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifdef _WIN32 +#define lseek _lseeki64 +#define ENOTSUP 4096 +/* XXX: find 64 bit version */ +#define ftruncate chsize + +static inline char *realpath(const char *path, char *resolved_path) +{ + _fullpath(resolved_path, path, _MAX_PATH); + return resolved_path; +} +#endif + +#ifdef QEMU_TOOL + +/* we use QEMU_TOOL in the command line tools which do not depend on + the target CPU type */ +#include "config-host.h" +#include +#include "osdep.h" +#include "bswap.h" + +#else + +#include "cpu.h" +#include "gdbstub.h" + +#endif /* !defined(QEMU_TOOL) */ + +#ifndef glue +#define xglue(x, y) x ## y +#define glue(x, y) xglue(x, y) +#define stringify(s) tostring(s) +#define tostring(s) #s +#endif + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +/* vl.c */ +uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c); + +void hw_error(const char *fmt, ...); + +extern const char *bios_dir; + +void pstrcpy(char *buf, int buf_size, const char *str); +char *pstrcat(char *buf, int buf_size, const char *s); +int strstart(const char *str, const char *val, const char **ptr); + +extern int vm_running; + +typedef struct vm_change_state_entry VMChangeStateEntry; +typedef void VMChangeStateHandler(void *opaque, int running); +typedef void VMStopHandler(void *opaque, int reason); + +VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, + void *opaque); +void qemu_del_vm_change_state_handler(VMChangeStateEntry *e); + +int qemu_add_vm_stop_handler(VMStopHandler *cb, void *opaque); +void qemu_del_vm_stop_handler(VMStopHandler *cb, void *opaque); + +void vm_start(void); +void vm_stop(int reason); + +typedef void QEMUResetHandler(void *opaque); + +void qemu_register_reset(QEMUResetHandler *func, void *opaque); +void qemu_system_reset_request(void); +void qemu_system_reset(void); +void qemu_system_shutdown_request(void); +void qemu_system_powerdown_request(void); +#if !defined(TARGET_SPARC) +// Please implement a power failure function to signal the OS +#define qemu_system_powerdown() do{}while(0) +#else +void qemu_system_powerdown(void); +#endif + +extern int reset_requested; + +void main_loop_wait(int timeout); + +int unset_mm_mapping(int xc_handle, uint32_t domid, unsigned long nr_pages, + unsigned int address_bits, unsigned long *extent_start); +int set_mm_mapping(int xc_handle, uint32_t domid, unsigned long nr_pages, + unsigned int address_bits, unsigned long *extent_start); + +extern void *shared_vram; + +extern FILE *logfile; + +extern int xc_handle; +extern int domid; + +extern uint64_t ram_size; +extern int bios_size; +extern int rtc_utc; +extern int cirrus_vga_enabled; +extern int graphic_width; +extern int graphic_height; +extern int graphic_depth; +extern const char *keyboard_layout; +extern int kqemu_allowed; +extern int win2k_install_hack; +extern int usb_enabled; +extern int smp_cpus; + +/* XXX: make it dynamic */ +#if defined (TARGET_PPC) +#define BIOS_SIZE ((512 + 32) * 1024) +#elif defined(TARGET_MIPS) +#define BIOS_SIZE (128 * 1024) +#else +#define BIOS_SIZE ((256 + 64) * 1024) +#endif + +/* keyboard/mouse support */ + +#define MOUSE_EVENT_LBUTTON 0x01 +#define MOUSE_EVENT_RBUTTON 0x02 +#define MOUSE_EVENT_MBUTTON 0x04 + +typedef void QEMUPutKBDEvent(void *opaque, int keycode); +typedef void QEMUPutMouseEvent(void *opaque, int dx, int dy, int dz, int buttons_state); + +void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque); +void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute); + +void kbd_put_keycode(int keycode); +void kbd_mouse_event(int dx, int dy, int dz, int buttons_state); +int kbd_mouse_is_absolute(void); + +/* keysym is a unicode code except for special keys (see QEMU_KEY_xxx + constants) */ +#define QEMU_KEY_ESC1(c) ((c) | 0xe100) +#define QEMU_KEY_BACKSPACE 0x007f +#define QEMU_KEY_UP QEMU_KEY_ESC1('A') +#define QEMU_KEY_DOWN QEMU_KEY_ESC1('B') +#define QEMU_KEY_RIGHT QEMU_KEY_ESC1('C') +#define QEMU_KEY_LEFT QEMU_KEY_ESC1('D') +#define QEMU_KEY_HOME QEMU_KEY_ESC1(1) +#define QEMU_KEY_END QEMU_KEY_ESC1(4) +#define QEMU_KEY_PAGEUP QEMU_KEY_ESC1(5) +#define QEMU_KEY_PAGEDOWN QEMU_KEY_ESC1(6) +#define QEMU_KEY_DELETE QEMU_KEY_ESC1(3) + +#define QEMU_KEY_CTRL_UP 0xe400 +#define QEMU_KEY_CTRL_DOWN 0xe401 +#define QEMU_KEY_CTRL_LEFT 0xe402 +#define QEMU_KEY_CTRL_RIGHT 0xe403 +#define QEMU_KEY_CTRL_HOME 0xe404 +#define QEMU_KEY_CTRL_END 0xe405 +#define QEMU_KEY_CTRL_PAGEUP 0xe406 +#define QEMU_KEY_CTRL_PAGEDOWN 0xe407 + +void kbd_put_keysym(int keysym); + +/* async I/O support */ + +typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size); +typedef int IOCanRWHandler(void *opaque); +typedef void IOHandler(void *opaque); + +int qemu_set_fd_handler2(int fd, + IOCanRWHandler *fd_read_poll, + IOHandler *fd_read, + IOHandler *fd_write, + void *opaque); +int qemu_set_fd_handler(int fd, + IOHandler *fd_read, + IOHandler *fd_write, + void *opaque); + +/* Polling handling */ + +/* return TRUE if no sleep should be done afterwards */ +typedef int PollingFunc(void *opaque); + +int qemu_add_polling_cb(PollingFunc *func, void *opaque); +void qemu_del_polling_cb(PollingFunc *func, void *opaque); + +/* character device */ + +#define CHR_EVENT_BREAK 0 /* serial break char */ +#define CHR_EVENT_FOCUS 1 /* focus to this terminal (modal input needed) */ + + + +#define CHR_IOCTL_SERIAL_SET_PARAMS 1 +typedef struct { + int speed; + int parity; + int data_bits; + int stop_bits; +} QEMUSerialSetParams; + +#define CHR_IOCTL_SERIAL_SET_BREAK 2 + +#define CHR_IOCTL_PP_READ_DATA 3 +#define CHR_IOCTL_PP_WRITE_DATA 4 +#define CHR_IOCTL_PP_READ_CONTROL 5 +#define CHR_IOCTL_PP_WRITE_CONTROL 6 +#define CHR_IOCTL_PP_READ_STATUS 7 + +typedef void IOEventHandler(void *opaque, int event); + +typedef struct CharDriverState { + int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len); + void (*chr_add_read_handler)(struct CharDriverState *s, + IOCanRWHandler *fd_can_read, + IOReadHandler *fd_read, void *opaque); + int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg); + IOEventHandler *chr_event; + void (*chr_send_event)(struct CharDriverState *chr, int event); + void (*chr_close)(struct CharDriverState *chr); + void *opaque; +} CharDriverState; + +void qemu_chr_printf(CharDriverState *s, const char *fmt, ...); +int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len); +void qemu_chr_send_event(CharDriverState *s, int event); +void qemu_chr_add_read_handler(CharDriverState *s, + IOCanRWHandler *fd_can_read, + IOReadHandler *fd_read, void *opaque); +void qemu_chr_add_event_handler(CharDriverState *s, IOEventHandler *chr_event); +int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg); + +/* consoles */ + +typedef struct DisplayState DisplayState; +typedef struct TextConsole TextConsole; + +typedef void (*vga_hw_update_ptr)(void *); +typedef void (*vga_hw_invalidate_ptr)(void *); +typedef void (*vga_hw_screen_dump_ptr)(void *, const char *); + +TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update, + vga_hw_invalidate_ptr invalidate, + vga_hw_screen_dump_ptr screen_dump, + void *opaque); +void vga_hw_update(void); +void vga_hw_invalidate(void); +void vga_hw_screen_dump(const char *filename); + +int is_graphic_console(void); +CharDriverState *text_console_init(DisplayState *ds); +void console_select(unsigned int index); + +/* serial ports */ + +#define MAX_SERIAL_PORTS 4 + +extern CharDriverState *serial_hds[MAX_SERIAL_PORTS]; + +/* parallel ports */ + +#define MAX_PARALLEL_PORTS 3 + +extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; + +/* VLANs support */ + +typedef struct VLANClientState VLANClientState; + +struct VLANClientState { + IOReadHandler *fd_read; + /* Packets may still be sent if this returns zero. It's used to + rate-limit the slirp code. */ + IOCanRWHandler *fd_can_read; + void *opaque; + struct VLANClientState *next; + struct VLANState *vlan; + char info_str[256]; +}; + +typedef struct VLANState { + int id; + VLANClientState *first_client; + struct VLANState *next; +} VLANState; + +VLANState *qemu_find_vlan(int id); +VLANClientState *qemu_new_vlan_client(VLANState *vlan, + IOReadHandler *fd_read, + IOCanRWHandler *fd_can_read, + void *opaque); +int qemu_can_send_packet(VLANClientState *vc); +void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size); +void qemu_handler_true(void *opaque); + +void do_info_network(void); + +/* TAP win32 */ +int tap_win32_init(VLANState *vlan, const char *ifname); +void tap_win32_poll(void); + +/* NIC info */ + +#define MAX_NICS 8 + +typedef struct NICInfo { + uint8_t macaddr[6]; + const char *model; + VLANState *vlan; +} NICInfo; + +extern int nb_nics; +extern NICInfo nd_table[MAX_NICS]; + +/* timers */ + +typedef struct QEMUClock QEMUClock; +typedef struct QEMUTimer QEMUTimer; +typedef void QEMUTimerCB(void *opaque); + +/* The real time clock should be used only for stuff which does not + change the virtual machine state, as it is run even if the virtual + machine is stopped. The real time clock has a frequency of 1000 + Hz. */ +extern QEMUClock *rt_clock; + +/* The virtual clock is only run during the emulation. It is stopped + when the virtual machine is stopped. Virtual timers use a high + precision clock, usually cpu cycles (use ticks_per_sec). */ +extern QEMUClock *vm_clock; + +int64_t qemu_get_clock(QEMUClock *clock); + +QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque); +void qemu_free_timer(QEMUTimer *ts); +void qemu_del_timer(QEMUTimer *ts); +void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time); +int qemu_timer_pending(QEMUTimer *ts); + +extern int64_t ticks_per_sec; +extern int pit_min_timer_count; + +void cpu_enable_ticks(void); +void cpu_disable_ticks(void); + +/* VM Load/Save */ + +typedef FILE QEMUFile; + +void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); +void qemu_put_byte(QEMUFile *f, int v); +void qemu_put_be16(QEMUFile *f, unsigned int v); +void qemu_put_be32(QEMUFile *f, unsigned int v); +void qemu_put_be64(QEMUFile *f, uint64_t v); +int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size); +int qemu_get_byte(QEMUFile *f); +unsigned int qemu_get_be16(QEMUFile *f); +unsigned int qemu_get_be32(QEMUFile *f); +uint64_t qemu_get_be64(QEMUFile *f); + +static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv) +{ + qemu_put_be64(f, *pv); +} + +static inline void qemu_put_be32s(QEMUFile *f, const uint32_t *pv) +{ + qemu_put_be32(f, *pv); +} + +static inline void qemu_put_be16s(QEMUFile *f, const uint16_t *pv) +{ + qemu_put_be16(f, *pv); +} + +static inline void qemu_put_8s(QEMUFile *f, const uint8_t *pv) +{ + qemu_put_byte(f, *pv); +} + +static inline void qemu_get_be64s(QEMUFile *f, uint64_t *pv) +{ + *pv = qemu_get_be64(f); +} + +static inline void qemu_get_be32s(QEMUFile *f, uint32_t *pv) +{ + *pv = qemu_get_be32(f); +} + +static inline void qemu_get_be16s(QEMUFile *f, uint16_t *pv) +{ + *pv = qemu_get_be16(f); +} + +static inline void qemu_get_8s(QEMUFile *f, uint8_t *pv) +{ + *pv = qemu_get_byte(f); +} + +#if TARGET_LONG_BITS == 64 +#define qemu_put_betl qemu_put_be64 +#define qemu_get_betl qemu_get_be64 +#define qemu_put_betls qemu_put_be64s +#define qemu_get_betls qemu_get_be64s +#else +#define qemu_put_betl qemu_put_be32 +#define qemu_get_betl qemu_get_be32 +#define qemu_put_betls qemu_put_be32s +#define qemu_get_betls qemu_get_be32s +#endif + +int64_t qemu_ftell(QEMUFile *f); +int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence); + +typedef void SaveStateHandler(QEMUFile *f, void *opaque); +typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); + +int qemu_loadvm(const char *filename); +int qemu_savevm(const char *filename); +int register_savevm(const char *idstr, + int instance_id, + int version_id, + SaveStateHandler *save_state, + LoadStateHandler *load_state, + void *opaque); +void qemu_get_timer(QEMUFile *f, QEMUTimer *ts); +void qemu_put_timer(QEMUFile *f, QEMUTimer *ts); + +void cpu_save(QEMUFile *f, void *opaque); +int cpu_load(QEMUFile *f, void *opaque, int version_id); + +/* block.c */ +typedef struct BlockDriverState BlockDriverState; +typedef struct BlockDriver BlockDriver; + +extern BlockDriver bdrv_raw; +extern BlockDriver bdrv_cow; +extern BlockDriver bdrv_qcow; +extern BlockDriver bdrv_vmdk; +extern BlockDriver bdrv_cloop; +extern BlockDriver bdrv_dmg; +extern BlockDriver bdrv_bochs; +extern BlockDriver bdrv_vpc; +extern BlockDriver bdrv_vvfat; + +void bdrv_init(void); +BlockDriver *bdrv_find_format(const char *format_name); +int bdrv_create(BlockDriver *drv, + const char *filename, int64_t size_in_sectors, + const char *backing_file, int flags); +BlockDriverState *bdrv_new(const char *device_name); +void bdrv_delete(BlockDriverState *bs); +int bdrv_open(BlockDriverState *bs, const char *filename, int snapshot); +int bdrv_open2(BlockDriverState *bs, const char *filename, int snapshot, + BlockDriver *drv); +void bdrv_close(BlockDriverState *bs); +int bdrv_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors); +int bdrv_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors); +void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr); +int bdrv_commit(BlockDriverState *bs); +void bdrv_set_boot_sector(BlockDriverState *bs, const uint8_t *data, int size); + +#define BDRV_TYPE_HD 0 +#define BDRV_TYPE_CDROM 1 +#define BDRV_TYPE_FLOPPY 2 +#define BIOS_ATA_TRANSLATION_AUTO 0 +#define BIOS_ATA_TRANSLATION_NONE 1 +#define BIOS_ATA_TRANSLATION_LBA 2 + +void bdrv_set_geometry_hint(BlockDriverState *bs, + int cyls, int heads, int secs); +void bdrv_set_type_hint(BlockDriverState *bs, int type); +void bdrv_set_translation_hint(BlockDriverState *bs, int translation); +void bdrv_get_geometry_hint(BlockDriverState *bs, + int *pcyls, int *pheads, int *psecs); +int bdrv_get_type_hint(BlockDriverState *bs); +int bdrv_get_translation_hint(BlockDriverState *bs); +int bdrv_is_removable(BlockDriverState *bs); +int bdrv_is_read_only(BlockDriverState *bs); +int bdrv_is_inserted(BlockDriverState *bs); +int bdrv_is_locked(BlockDriverState *bs); +void bdrv_set_locked(BlockDriverState *bs, int locked); +void bdrv_set_change_cb(BlockDriverState *bs, + void (*change_cb)(void *opaque), void *opaque); +void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size); +void bdrv_info(void); +BlockDriverState *bdrv_find(const char *name); +void bdrv_iterate(void (*it)(void *opaque, const char *name), void *opaque); +int bdrv_is_encrypted(BlockDriverState *bs); +int bdrv_set_key(BlockDriverState *bs, const char *key); +void bdrv_iterate_format(void (*it)(void *opaque, const char *name), + void *opaque); +const char *bdrv_get_device_name(BlockDriverState *bs); + +int qcow_get_cluster_size(BlockDriverState *bs); +int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf); + +#ifndef QEMU_TOOL + +typedef void QEMUMachineInitFunc(uint64_t ram_size, int vga_ram_size, + int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, time_t timeoffset); + +typedef struct QEMUMachine { + const char *name; + const char *desc; + QEMUMachineInitFunc *init; + struct QEMUMachine *next; +} QEMUMachine; + +int qemu_register_machine(QEMUMachine *m); + +typedef void SetIRQFunc(void *opaque, int irq_num, int level); +typedef void IRQRequestFunc(void *opaque, int level); + +/* ISA bus */ + +extern target_phys_addr_t isa_mem_base; + +typedef void (IOPortWriteFunc)(void *opaque, uint32_t address, uint32_t data); +typedef uint32_t (IOPortReadFunc)(void *opaque, uint32_t address); + +int register_ioport_read(int start, int length, int size, + IOPortReadFunc *func, void *opaque); +int register_ioport_write(int start, int length, int size, + IOPortWriteFunc *func, void *opaque); +void isa_unassign_ioport(int start, int length); + +/* PCI bus */ + +extern target_phys_addr_t pci_mem_base; + +typedef struct PCIBus PCIBus; +typedef struct PCIDevice PCIDevice; + +typedef void PCIConfigWriteFunc(PCIDevice *pci_dev, + uint32_t address, uint32_t data, int len); +typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev, + uint32_t address, int len); +typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type); + +#define PCI_ADDRESS_SPACE_MEM 0x00 +#define PCI_ADDRESS_SPACE_IO 0x01 +#define PCI_ADDRESS_SPACE_MEM_PREFETCH 0x08 + +typedef struct PCIIORegion { + uint32_t addr; /* current PCI mapping address. -1 means not mapped */ + uint32_t size; + uint8_t type; + PCIMapIORegionFunc *map_func; +} PCIIORegion; + +#define PCI_ROM_SLOT 6 +#define PCI_NUM_REGIONS 7 +struct PCIDevice { + /* PCI config space */ + uint8_t config[256]; + + /* the following fields are read only */ + PCIBus *bus; + int devfn; + char name[64]; + PCIIORegion io_regions[PCI_NUM_REGIONS]; + + /* do not access the following fields */ + PCIConfigReadFunc *config_read; + PCIConfigWriteFunc *config_write; + int irq_index; +}; + +PCIDevice *pci_register_device(PCIBus *bus, const char *name, + int instance_size, int devfn, + PCIConfigReadFunc *config_read, + PCIConfigWriteFunc *config_write); + +void pci_register_io_region(PCIDevice *pci_dev, int region_num, + uint32_t size, int type, + PCIMapIORegionFunc *map_func); + +void pci_set_irq(PCIDevice *pci_dev, int irq_num, int level); + +uint32_t pci_default_read_config(PCIDevice *d, + uint32_t address, int len); +void pci_default_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len); +void generic_pci_save(QEMUFile* f, void *opaque); +int generic_pci_load(QEMUFile* f, void *opaque, int version_id); + +extern struct PIIX3State *piix3_state; + +PCIBus *i440fx_init(void); +void piix3_init(PCIBus *bus); +void pci_bios_init(void); +void pci_info(void); + +/* temporary: will be moved in platform specific file */ +void pci_set_pic(PCIBus *bus, SetIRQFunc *set_irq, void *irq_opaque); +PCIBus *pci_prep_init(void); +PCIBus *pci_grackle_init(uint32_t base); +PCIBus *pci_pmac_init(void); +PCIBus *pci_apb_init(target_ulong special_base, target_ulong mem_base); + +void pci_nic_init(PCIBus *bus, NICInfo *nd); + +/* openpic.c */ +typedef struct openpic_t openpic_t; +void openpic_set_irq(void *opaque, int n_IRQ, int level); +openpic_t *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus, + CPUState **envp); + +/* heathrow_pic.c */ +typedef struct HeathrowPICS HeathrowPICS; +void heathrow_pic_set_irq(void *opaque, int num, int level); +HeathrowPICS *heathrow_pic_init(int *pmem_index); + +#ifdef HAS_AUDIO +struct soundhw { + const char *name; + const char *descr; + int enabled; + int isa; + union { + int (*init_isa) (AudioState *s); + int (*init_pci) (PCIBus *bus, AudioState *s); + } init; +}; + +extern struct soundhw soundhw[]; +#endif + +/* vga.c */ + +#define VGA_RAM_SIZE (4096 * 1024) + +struct DisplayState { + uint8_t *data; + int linesize; + int depth; + int width; + int height; + void *opaque; + + void (*dpy_update)(struct DisplayState *s, int x, int y, int w, int h); + void (*dpy_resize)(struct DisplayState *s, int w, int h); + void (*dpy_refresh)(struct DisplayState *s); + void (*dpy_copy)(struct DisplayState *s, int src_x, int src_y, int dst_x, int dst_y, int w, int h); +}; + +static inline void dpy_update(DisplayState *s, int x, int y, int w, int h) +{ + s->dpy_update(s, x, y, w, h); +} + +static inline void dpy_resize(DisplayState *s, int w, int h) +{ + s->dpy_resize(s, w, h); +} + +int vga_initialize(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, + unsigned long vga_ram_offset, int vga_ram_size, + unsigned long vga_bios_offset, int vga_bios_size); + +/* cirrus_vga.c */ +void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, + unsigned long vga_ram_offset, int vga_ram_size); +void isa_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base, + unsigned long vga_ram_offset, int vga_ram_size); + +/* sdl.c */ +void sdl_display_init(DisplayState *ds, int full_screen); + +/* cocoa.m */ +void cocoa_display_init(DisplayState *ds, int full_screen); + +/* vnc.c */ +void vnc_display_init(DisplayState *ds, int display); + +/* ide.c */ +#define MAX_DISKS 4 + +extern BlockDriverState *bs_table[MAX_DISKS]; + +void isa_ide_init(int iobase, int iobase2, int irq, + BlockDriverState *hd0, BlockDriverState *hd1); +void pci_cmd646_ide_init(PCIBus *bus, BlockDriverState **hd_table, + int secondary_ide_enabled); +void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table); +int pmac_ide_init (BlockDriverState **hd_table, + SetIRQFunc *set_irq, void *irq_opaque, int irq); + +/* es1370.c */ +int es1370_init (PCIBus *bus, AudioState *s); + +/* sb16.c */ +int SB16_init (AudioState *s); + +/* adlib.c */ +int Adlib_init (AudioState *s); + +/* gus.c */ +int GUS_init (AudioState *s); + +/* dma.c */ +typedef int (*DMA_transfer_handler) (void *opaque, int nchan, int pos, int size); +int DMA_get_channel_mode (int nchan); +int DMA_read_memory (int nchan, void *buf, int pos, int size); +int DMA_write_memory (int nchan, void *buf, int pos, int size); +void DMA_hold_DREQ (int nchan); +void DMA_release_DREQ (int nchan); +void DMA_schedule(int nchan); +void DMA_run (void); +void DMA_init (int high_page_enable); +void DMA_register_channel (int nchan, + DMA_transfer_handler transfer_handler, + void *opaque); +/* fdc.c */ +#define MAX_FD 2 +extern BlockDriverState *fd_table[MAX_FD]; + +typedef struct fdctrl_t fdctrl_t; + +fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped, + uint32_t io_base, + BlockDriverState **fds); +int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num); + +/* ne2000.c */ + +void isa_ne2000_init(int base, int irq, NICInfo *nd); +void pci_ne2000_init(PCIBus *bus, NICInfo *nd); + +/* rtl8139.c */ + +void pci_rtl8139_init(PCIBus *bus, NICInfo *nd); + +/* pckbd.c */ + +void kbd_init(void); + +/* mc146818rtc.c */ + +typedef struct RTCState RTCState; + +RTCState *rtc_init(int base, int irq); +void rtc_set_memory(RTCState *s, int addr, int val); +void rtc_set_date(RTCState *s, const struct tm *tm); + +/* serial.c */ + +typedef struct SerialState SerialState; +SerialState *serial_init(SetIRQFunc *set_irq, void *opaque, + int base, int irq, CharDriverState *chr); +SerialState *serial_mm_init (SetIRQFunc *set_irq, void *opaque, + target_ulong base, int it_shift, + int irq, CharDriverState *chr); + +/* parallel.c */ + +typedef struct ParallelState ParallelState; +ParallelState *parallel_init(int base, int irq, CharDriverState *chr); + +/* i8259.c */ + +typedef struct PicState2 PicState2; +extern PicState2 *isa_pic; +void pic_set_irq(int irq, int level); +void pic_set_irq_new(void *opaque, int irq, int level); +PicState2 *pic_init(IRQRequestFunc *irq_request, void *irq_request_opaque); +void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func, + void *alt_irq_opaque); +int pic_read_irq(PicState2 *s); +void pic_update_irq(PicState2 *s); +uint32_t pic_intack_read(PicState2 *s); +void pic_info(void); +void irq_info(void); +void sp_info(void); + +/* APIC */ +typedef struct IOAPICState IOAPICState; + +int apic_init(CPUState *env); +int apic_get_interrupt(CPUState *env); +IOAPICState *ioapic_init(void); +void ioapic_set_irq(void *opaque, int vector, int level); + +/* i8254.c */ + +#define PIT_FREQ 1193182 + +typedef struct PITState PITState; + +PITState *pit_init(int base, int irq); +void pit_set_gate(PITState *pit, int channel, int val); +int pit_get_gate(PITState *pit, int channel); +int pit_get_initial_count(PITState *pit, int channel); +int pit_get_mode(PITState *pit, int channel); +int pit_get_out(PITState *pit, int channel, int64_t current_time); + +/* pcspk.c */ +void pcspk_init(PITState *); +int pcspk_audio_init(AudioState *); + +/* pc.c */ +extern QEMUMachine pc_machine; +extern QEMUMachine isapc_machine; + +void ioport_set_a20(int enable); +int ioport_get_a20(void); + +/* ppc.c */ +extern QEMUMachine prep_machine; +extern QEMUMachine core99_machine; +extern QEMUMachine heathrow_machine; + +/* mips_r4k.c */ +extern QEMUMachine mips_machine; + +/* shix.c */ +extern QEMUMachine shix_machine; + +#ifdef TARGET_PPC +ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq); +#endif +void PREP_debug_write (void *opaque, uint32_t addr, uint32_t val); + +extern CPUWriteMemoryFunc *PPC_io_write[]; +extern CPUReadMemoryFunc *PPC_io_read[]; +void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val); + +/* sun4m.c */ +extern QEMUMachine sun4m_machine; +uint32_t iommu_translate(uint32_t addr); +void pic_set_irq_cpu(int irq, int level, unsigned int cpu); + +/* iommu.c */ +void *iommu_init(uint32_t addr); +uint32_t iommu_translate_local(void *opaque, uint32_t addr); + +/* lance.c */ +void lance_init(NICInfo *nd, int irq, uint32_t leaddr, uint32_t ledaddr); + +/* tcx.c */ +void tcx_init(DisplayState *ds, uint32_t addr, uint8_t *vram_base, + unsigned long vram_offset, int vram_size, int width, int height); + +/* slavio_intctl.c */ +void *slavio_intctl_init(void); +void slavio_intctl_set_cpu(void *opaque, unsigned int cpu, CPUState *env); +void slavio_pic_info(void *opaque); +void slavio_irq_info(void *opaque); +void slavio_pic_set_irq(void *opaque, int irq, int level); +void slavio_pic_set_irq_cpu(void *opaque, int irq, int level, unsigned int cpu); + +/* loader.c */ +int get_image_size(const char *filename); +int load_image(const char *filename, uint8_t *addr); +int load_elf(const char *filename, int64_t virt_to_phys_addend, uint64_t *pentry); +int load_aout(const char *filename, uint8_t *addr); + +/* slavio_timer.c */ +void slavio_timer_init(uint32_t addr, int irq, int mode, unsigned int cpu); + +/* slavio_serial.c */ +SerialState *slavio_serial_init(int base, int irq, CharDriverState *chr1, CharDriverState *chr2); +void slavio_serial_ms_kbd_init(int base, int irq); + +/* slavio_misc.c */ +void *slavio_misc_init(uint32_t base, int irq); +void slavio_set_power_fail(void *opaque, int power_failing); + +/* esp.c */ +void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdaddr); + +/* sun4u.c */ +extern QEMUMachine sun4u_machine; + +/* NVRAM helpers */ +#include "hw/m48t59.h" + +void NVRAM_set_byte (m48t59_t *nvram, uint32_t addr, uint8_t value); +uint8_t NVRAM_get_byte (m48t59_t *nvram, uint32_t addr); +void NVRAM_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value); +uint16_t NVRAM_get_word (m48t59_t *nvram, uint32_t addr); +void NVRAM_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value); +uint32_t NVRAM_get_lword (m48t59_t *nvram, uint32_t addr); +void NVRAM_set_string (m48t59_t *nvram, uint32_t addr, + const unsigned char *str, uint32_t max); +int NVRAM_get_string (m48t59_t *nvram, uint8_t *dst, uint16_t addr, int max); +void NVRAM_set_crc (m48t59_t *nvram, uint32_t addr, + uint32_t start, uint32_t count); +int PPC_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size, + const unsigned char *arch, + uint32_t RAM_size, int boot_device, + uint32_t kernel_image, uint32_t kernel_size, + const char *cmdline, + uint32_t initrd_image, uint32_t initrd_size, + uint32_t NVRAM_image, + int width, int height, int depth); + +/* adb.c */ + +#define MAX_ADB_DEVICES 16 + +#define ADB_MAX_OUT_LEN 16 + +typedef struct ADBDevice ADBDevice; + +/* buf = NULL means polling */ +typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out, + const uint8_t *buf, int len); +typedef int ADBDeviceReset(ADBDevice *d); + +struct ADBDevice { + struct ADBBusState *bus; + int devaddr; + int handler; + ADBDeviceRequest *devreq; + ADBDeviceReset *devreset; + void *opaque; +}; + +typedef struct ADBBusState { + ADBDevice devices[MAX_ADB_DEVICES]; + int nb_devices; + int poll_index; +} ADBBusState; + +int adb_request(ADBBusState *s, uint8_t *buf_out, + const uint8_t *buf, int len); +int adb_poll(ADBBusState *s, uint8_t *buf_out); + +ADBDevice *adb_register_device(ADBBusState *s, int devaddr, + ADBDeviceRequest *devreq, + ADBDeviceReset *devreset, + void *opaque); +void adb_kbd_init(ADBBusState *bus); +void adb_mouse_init(ADBBusState *bus); + +/* cuda.c */ + +extern ADBBusState adb_bus; +int cuda_init(SetIRQFunc *set_irq, void *irq_opaque, int irq); + +#include "hw/usb.h" + +/* usb ports of the VM */ + +#define MAX_VM_USB_PORTS 8 + +extern USBPort *vm_usb_ports[MAX_VM_USB_PORTS]; +extern USBDevice *vm_usb_hub; + +void do_usb_add(const char *devname); +void do_usb_del(const char *devname); +void usb_info(void); + +/* integratorcp.c */ +extern QEMUMachine integratorcp926_machine; +extern QEMUMachine integratorcp1026_machine; + +/* versatilepb.c */ +extern QEMUMachine versatilepb_machine; +extern QEMUMachine versatileab_machine; + +/* ps2.c */ +void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg); +void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg); +void ps2_write_mouse(void *, int val); +void ps2_write_keyboard(void *, int val); +uint32_t ps2_read_data(void *); +void ps2_queue(void *, int b); +void ps2_keyboard_set_translation(void *opaque, int mode); + +/* smc91c111.c */ +void smc91c111_init(NICInfo *, uint32_t, void *, int); + +/* pl110.c */ +void *pl110_init(DisplayState *ds, uint32_t base, void *pic, int irq, int); + +/* pl011.c */ +void pl011_init(uint32_t base, void *pic, int irq, CharDriverState *chr); + +/* pl050.c */ +void pl050_init(uint32_t base, void *pic, int irq, int is_mouse); + +/* pl080.c */ +void *pl080_init(uint32_t base, void *pic, int irq); + +/* pl190.c */ +void *pl190_init(uint32_t base, void *parent, int irq, int fiq); + +/* arm-timer.c */ +void sp804_init(uint32_t base, void *pic, int irq); +void icp_pit_init(uint32_t base, void *pic, int irq); + +/* arm_boot.c */ + +void arm_load_kernel(int ram_size, const char *kernel_filename, + const char *kernel_cmdline, const char *initrd_filename, + int board_id); + +/* sh7750.c */ +struct SH7750State; + +struct SH7750State *sh7750_init(CPUState * cpu); + +typedef struct { + /* The callback will be triggered if any of the designated lines change */ + uint16_t portamask_trigger; + uint16_t portbmask_trigger; + /* Return 0 if no action was taken */ + int (*port_change_cb) (uint16_t porta, uint16_t portb, + uint16_t * periph_pdtra, + uint16_t * periph_portdira, + uint16_t * periph_pdtrb, + uint16_t * periph_portdirb); +} sh7750_io_device; + +int sh7750_register_io_device(struct SH7750State *s, + sh7750_io_device * device); +/* tc58128.c */ +int tc58128_init(struct SH7750State *s, char *zone1, char *zone2); + +#endif /* defined(QEMU_TOOL) */ + +/* monitor.c */ +void monitor_init(CharDriverState *hd, int show_banner); +void term_puts(const char *str); +void term_vprintf(const char *fmt, va_list ap); +void term_printf(const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +void term_flush(void); +void term_print_help(void); +void monitor_readline(const char *prompt, int is_password, + char *buf, int buf_size); + +/* readline.c */ +typedef void ReadLineFunc(void *opaque, const char *str); + +extern int completion_index; +void add_completion(const char *str); +void readline_handle_byte(int ch); +void readline_find_completion(const char *cmdline); +const char *readline_get_history(unsigned int index); +void readline_start(const char *prompt, int is_password, + ReadLineFunc *readline_func, void *opaque); + +void kqemu_record_dump(void); + +extern char domain_name[]; + +void destroy_hvm_domain(void); + +#endif /* VL_H */ diff --git a/tools/ioemu/vnc.c b/tools/ioemu/vnc.c new file mode 100644 index 0000000000..4a516306cb --- /dev/null +++ b/tools/ioemu/vnc.c @@ -0,0 +1,1001 @@ +/* + * QEMU VNC display driver + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2006 Christian Limpach + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "vl.h" +#include "qemu_socket.h" + +#define VNC_REFRESH_INTERVAL (1000 / 30) + +#include "vnc_keysym.h" +#include "keymaps.c" + +typedef struct Buffer +{ + size_t capacity; + size_t offset; + char *buffer; +} Buffer; + +typedef struct VncState VncState; + +typedef int VncReadEvent(VncState *vs, char *data, size_t len); + +struct VncState +{ + QEMUTimer *timer; + int lsock; + int csock; + DisplayState *ds; + int need_update; + int width; + int height; + uint64_t *dirty_row; /* screen regions which are possibly dirty */ + int dirty_pixel_shift; + uint64_t *update_row; /* outstanding updates */ + int has_update; /* there's outstanding updates in the + * visible area */ + char *old_data; + int depth; + int has_resize; + int has_hextile; + Buffer output; + Buffer input; + kbd_layout_t *kbd_layout; + + VncReadEvent *read_handler; + size_t read_handler_expect; + + int visible_x; + int visible_y; + int visible_w; + int visible_h; + + int slow_client; +}; + +#define DIRTY_PIXEL_BITS 64 +#define X2DP_DOWN(vs, x) ((x) >> (vs)->dirty_pixel_shift) +#define X2DP_UP(vs, x) \ + (((x) + (1ULL << (vs)->dirty_pixel_shift) - 1) >> (vs)->dirty_pixel_shift) +#define DP2X(vs, x) ((x) << (vs)->dirty_pixel_shift) + +/* TODO + 1) Get the queue working for IO. + 2) there is some weirdness when using the -S option (the screen is grey + and not totally invalidated +*/ + +static void vnc_write(VncState *vs, const void *data, size_t len); +static void vnc_write_u32(VncState *vs, uint32_t value); +static void vnc_write_s32(VncState *vs, int32_t value); +static void vnc_write_u16(VncState *vs, uint16_t value); +static void vnc_write_u8(VncState *vs, uint8_t value); +static void vnc_flush(VncState *vs); +static void _vnc_update_client(void *opaque); +static void vnc_update_client(void *opaque); +static void vnc_client_read(void *opaque); +static void framebuffer_set_updated(VncState *vs, int x, int y, int w, int h); + +static void set_bits_in_row(VncState *vs, uint64_t *row, + int x, int y, int w, int h) +{ + int x1, x2; + uint64_t mask; + + if (w == 0) + return; + + x1 = X2DP_DOWN(vs, x); + x2 = X2DP_UP(vs, x + w); + + if (X2DP_UP(vs, w) != DIRTY_PIXEL_BITS) + mask = ((1ULL << (x2 - x1)) - 1) << x1; + else + mask = ~(0ULL); + + h += y; + for (; y < h; y++) + row[y] |= mask; +} + +static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) +{ + VncState *vs = ds->opaque; + + set_bits_in_row(vs, vs->dirty_row, x, y, w, h); +} + +static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, + int32_t encoding) +{ + vnc_write_u16(vs, x); + vnc_write_u16(vs, y); + vnc_write_u16(vs, w); + vnc_write_u16(vs, h); + + vnc_write_s32(vs, encoding); +} + +static void vnc_dpy_resize(DisplayState *ds, int w, int h) +{ + VncState *vs = ds->opaque; + int o; + + ds->data = realloc(ds->data, w * h * vs->depth); + vs->old_data = realloc(vs->old_data, w * h * vs->depth); + vs->dirty_row = realloc(vs->dirty_row, h * sizeof(vs->dirty_row[0])); + vs->update_row = realloc(vs->update_row, h * sizeof(vs->dirty_row[0])); + + if (ds->data == NULL || vs->old_data == NULL || + vs->dirty_row == NULL || vs->update_row == NULL) { + fprintf(stderr, "vnc: memory allocation failed\n"); + exit(1); + } + + ds->depth = vs->depth * 8; + ds->width = w; + ds->height = h; + ds->linesize = w * vs->depth; + if (vs->csock != -1 && vs->has_resize) { + vnc_write_u8(vs, 0); /* msg id */ + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); /* number of rects */ + vnc_framebuffer_update(vs, 0, 0, ds->width, ds->height, -223); + vnc_flush(vs); + vs->width = ds->width; + vs->height = ds->height; + } + vs->dirty_pixel_shift = 0; + for (o = DIRTY_PIXEL_BITS; o < ds->width; o *= 2) + vs->dirty_pixel_shift++; + framebuffer_set_updated(vs, 0, 0, ds->width, ds->height); +} + +static void send_framebuffer_update_raw(VncState *vs, int x, int y, int w, int h) +{ + int i; + char *row; + + vnc_framebuffer_update(vs, x, y, w, h, 0); + + row = vs->ds->data + y * vs->ds->linesize + x * vs->depth; + for (i = 0; i < h; i++) { + vnc_write(vs, row, w * vs->depth); + row += vs->ds->linesize; + } +} + +static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h) +{ + ptr[0] = ((x & 0x0F) << 4) | (y & 0x0F); + ptr[1] = (((w - 1) & 0x0F) << 4) | ((h - 1) & 0x0F); +} + +#define BPP 8 +#include "vnchextile.h" +#undef BPP + +#define BPP 16 +#include "vnchextile.h" +#undef BPP + +#define BPP 32 +#include "vnchextile.h" +#undef BPP + +static void send_framebuffer_update_hextile(VncState *vs, int x, int y, int w, int h) +{ + int i, j; + int has_fg, has_bg; + uint32_t last_fg32, last_bg32; + uint16_t last_fg16, last_bg16; + uint8_t last_fg8, last_bg8; + + vnc_framebuffer_update(vs, x, y, w, h, 5); + + has_fg = has_bg = 0; + for (j = y; j < (y + h); j += 16) { + for (i = x; i < (x + w); i += 16) { + switch (vs->depth) { + case 1: + send_hextile_tile_8(vs, i, j, MIN(16, x + w - i), MIN(16, y + h - j), + &last_bg8, &last_fg8, &has_bg, &has_fg); + break; + case 2: + send_hextile_tile_16(vs, i, j, MIN(16, x + w - i), MIN(16, y + h - j), + &last_bg16, &last_fg16, &has_bg, &has_fg); + break; + case 4: + send_hextile_tile_32(vs, i, j, MIN(16, x + w - i), MIN(16, y + h - j), + &last_bg32, &last_fg32, &has_bg, &has_fg); + break; + default: + break; + } + } + } +} + +static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +{ + if (vs->has_hextile) + send_framebuffer_update_hextile(vs, x, y, w, h); + else + send_framebuffer_update_raw(vs, x, y, w, h); +} + +static void vnc_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h) +{ + int src, dst; + char *src_row; + char *dst_row; + char *old_row; + int y = 0; + int pitch = ds->linesize; + VncState *vs = ds->opaque; + int updating_client = !vs->slow_client; + + if (src_x < vs->visible_x || src_y < vs->visible_y || + dst_x < vs->visible_x || dst_y < vs->visible_y || + (src_x + w) > (vs->visible_x + vs->visible_w) || + (src_y + h) > (vs->visible_y + vs->visible_h) || + (dst_x + w) > (vs->visible_x + vs->visible_w) || + (dst_y + h) > (vs->visible_y + vs->visible_h)) + updating_client = 0; + + if (updating_client) { + vs->need_update = 1; + _vnc_update_client(vs); + } + + if (dst_y > src_y) { + y = h - 1; + pitch = -pitch; + } + + src = (ds->linesize * (src_y + y) + vs->depth * src_x); + dst = (ds->linesize * (dst_y + y) + vs->depth * dst_x); + + src_row = ds->data + src; + dst_row = ds->data + dst; + old_row = vs->old_data + dst; + + for (y = 0; y < h; y++) { + memmove(old_row, src_row, w * vs->depth); + memmove(dst_row, src_row, w * vs->depth); + src_row += pitch; + dst_row += pitch; + old_row += pitch; + } + + if (updating_client && vs->csock != -1 && !vs->has_update) { + vnc_write_u8(vs, 0); /* msg id */ + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); /* number of rects */ + vnc_framebuffer_update(vs, dst_x, dst_y, w, h, 1); + vnc_write_u16(vs, src_x); + vnc_write_u16(vs, src_y); + vnc_flush(vs); + } else + framebuffer_set_updated(vs, dst_x, dst_y, w, h); +} + +static int find_update_height(VncState *vs, int y, int maxy, int last_x, int x) +{ + int h; + + for (h = 1; y + h < maxy; h++) { + int tmp_x; + if (!(vs->update_row[y + h] & (1ULL << last_x))) + break; + for (tmp_x = last_x; tmp_x < x; tmp_x++) + vs->update_row[y + h] &= ~(1ULL << tmp_x); + } + + return h; +} + +static void _vnc_update_client(void *opaque) +{ + VncState *vs = opaque; + int64_t now = qemu_get_clock(rt_clock); + + if (vs->need_update && vs->csock != -1) { + int y; + char *row; + char *old_row; + uint64_t width_mask; + int n_rectangles; + int saved_offset; + int maxx, maxy; + int tile_bytes = vs->depth * DP2X(vs, 1); + + if (vs->width != DP2X(vs, DIRTY_PIXEL_BITS)) + width_mask = (1ULL << X2DP_UP(vs, vs->ds->width)) - 1; + else + width_mask = ~(0ULL); + + /* Walk through the dirty map and eliminate tiles that + really aren't dirty */ + row = vs->ds->data; + old_row = vs->old_data; + + for (y = 0; y < vs->ds->height; y++) { + if (vs->dirty_row[y] & width_mask) { + int x; + char *ptr, *old_ptr; + + ptr = row; + old_ptr = old_row; + + for (x = 0; x < X2DP_UP(vs, vs->ds->width); x++) { + if (vs->dirty_row[y] & (1ULL << x)) { + if (memcmp(old_ptr, ptr, tile_bytes)) { + vs->has_update = 1; + vs->update_row[y] |= (1ULL << x); + memcpy(old_ptr, ptr, tile_bytes); + } + vs->dirty_row[y] &= ~(1ULL << x); + } + + ptr += tile_bytes; + old_ptr += tile_bytes; + } + } + + row += vs->ds->linesize; + old_row += vs->ds->linesize; + } + + if (!vs->has_update || vs->visible_y >= vs->ds->height || + vs->visible_x >= vs->ds->width) + goto out; + + /* Count rectangles */ + n_rectangles = 0; + vnc_write_u8(vs, 0); /* msg id */ + vnc_write_u8(vs, 0); + saved_offset = vs->output.offset; + vnc_write_u16(vs, 0); + + maxy = vs->visible_y + vs->visible_h; + if (maxy > vs->ds->height) + maxy = vs->ds->height; + maxx = vs->visible_x + vs->visible_w; + if (maxx > vs->ds->width) + maxx = vs->ds->width; + + for (y = vs->visible_y; y < maxy; y++) { + int x; + int last_x = -1; + for (x = X2DP_DOWN(vs, vs->visible_x); + x < X2DP_UP(vs, maxx); x++) { + if (vs->update_row[y] & (1ULL << x)) { + if (last_x == -1) + last_x = x; + vs->update_row[y] &= ~(1ULL << x); + } else { + if (last_x != -1) { + int h = find_update_height(vs, y, maxy, last_x, x); + send_framebuffer_update(vs, DP2X(vs, last_x), y, + DP2X(vs, (x - last_x)), h); + n_rectangles++; + } + last_x = -1; + } + } + if (last_x != -1) { + int h = find_update_height(vs, y, maxy, last_x, x); + send_framebuffer_update(vs, DP2X(vs, last_x), y, + DP2X(vs, (x - last_x)), h); + n_rectangles++; + } + } + vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF; + vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF; + + vs->has_update = 0; + vs->need_update = 0; + vnc_flush(vs); + vs->slow_client = 0; + } else + vs->slow_client = 1; + + out: + qemu_mod_timer(vs->timer, now + VNC_REFRESH_INTERVAL); +} + +static void vnc_update_client(void *opaque) +{ + VncState *vs = opaque; + + vs->ds->dpy_refresh(vs->ds); + _vnc_update_client(vs); +} + +static void vnc_timer_init(VncState *vs) +{ + if (vs->timer == NULL) { + vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs); + qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock)); + } +} + +static void vnc_dpy_refresh(DisplayState *ds) +{ + vga_hw_update(); +} + +static int vnc_listen_poll(void *opaque) +{ + VncState *vs = opaque; + if (vs->csock == -1) + return 1; + return 0; +} + +static void buffer_reserve(Buffer *buffer, size_t len) +{ + if ((buffer->capacity - buffer->offset) < len) { + buffer->capacity += (len + 1024); + buffer->buffer = realloc(buffer->buffer, buffer->capacity); + if (buffer->buffer == NULL) { + fprintf(stderr, "vnc: out of memory\n"); + exit(1); + } + } +} + +static int buffer_empty(Buffer *buffer) +{ + return buffer->offset == 0; +} + +static char *buffer_end(Buffer *buffer) +{ + return buffer->buffer + buffer->offset; +} + +static void buffer_reset(Buffer *buffer) +{ + buffer->offset = 0; +} + +static void buffer_append(Buffer *buffer, const void *data, size_t len) +{ + memcpy(buffer->buffer + buffer->offset, data, len); + buffer->offset += len; +} + +static int vnc_client_io_error(VncState *vs, int ret, int last_errno) +{ + if (ret == 0 || ret == -1) { + if (ret == -1 && (last_errno == EINTR || last_errno == EAGAIN)) + return 0; + + qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); + closesocket(vs->csock); + vs->csock = -1; + buffer_reset(&vs->input); + buffer_reset(&vs->output); + vs->need_update = 0; + return 0; + } + return ret; +} + +static void vnc_client_error(VncState *vs) +{ + vnc_client_io_error(vs, -1, EINVAL); +} + +static void vnc_client_write(void *opaque) +{ + ssize_t ret; + VncState *vs = opaque; + + ret = send(vs->csock, vs->output.buffer, vs->output.offset, 0); + ret = vnc_client_io_error(vs, ret, socket_error()); + if (!ret) + return; + + memmove(vs->output.buffer, vs->output.buffer + ret, + vs->output.offset - ret); + vs->output.offset -= ret; + + if (vs->output.offset == 0) + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); +} + +static void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting) +{ + vs->read_handler = func; + vs->read_handler_expect = expecting; +} + +static void vnc_client_read(void *opaque) +{ + VncState *vs = opaque; + ssize_t ret; + + buffer_reserve(&vs->input, 4096); + + ret = recv(vs->csock, buffer_end(&vs->input), 4096, 0); + ret = vnc_client_io_error(vs, ret, socket_error()); + if (!ret) + return; + + vs->input.offset += ret; + + while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) { + size_t len = vs->read_handler_expect; + int ret; + + ret = vs->read_handler(vs, vs->input.buffer, len); + if (vs->csock == -1) + return; + + if (!ret) { + memmove(vs->input.buffer, vs->input.buffer + len, + vs->input.offset - len); + vs->input.offset -= len; + } else + vs->read_handler_expect = ret; + } +} + +static void vnc_write(VncState *vs, const void *data, size_t len) +{ + buffer_reserve(&vs->output, len); + + if (buffer_empty(&vs->output)) + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, + vnc_client_write, vs); + + buffer_append(&vs->output, data, len); +} + +static void vnc_write_s32(VncState *vs, int32_t value) +{ + vnc_write_u32(vs, *(uint32_t *)&value); +} + +static void vnc_write_u32(VncState *vs, uint32_t value) +{ + uint8_t buf[4]; + + buf[0] = (value >> 24) & 0xFF; + buf[1] = (value >> 16) & 0xFF; + buf[2] = (value >> 8) & 0xFF; + buf[3] = value & 0xFF; + + vnc_write(vs, buf, 4); +} + +static void vnc_write_u16(VncState *vs, uint16_t value) +{ + char buf[2]; + + buf[0] = (value >> 8) & 0xFF; + buf[1] = value & 0xFF; + + vnc_write(vs, buf, 2); +} + +static void vnc_write_u8(VncState *vs, uint8_t value) +{ + vnc_write(vs, (char *)&value, 1); +} + +static void vnc_flush(VncState *vs) +{ + if (vs->output.offset) + vnc_client_write(vs); +} + +static uint8_t read_u8(char *data, size_t offset) +{ + return data[offset]; +} + +static uint16_t read_u16(char *data, size_t offset) +{ + return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF); +} + +static int32_t read_s32(char *data, size_t offset) +{ + return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) | + (data[offset + 2] << 8) | data[offset + 3]); +} + +static uint32_t read_u32(char *data, size_t offset) +{ + return ((data[offset] << 24) | (data[offset + 1] << 16) | + (data[offset + 2] << 8) | data[offset + 3]); +} + +static void client_cut_text(VncState *vs, size_t len, char *text) +{ +} + +static void pointer_event(VncState *vs, int button_mask, int x, int y) +{ + int buttons = 0; + int dz = 0; + + if (button_mask & 0x01) + buttons |= MOUSE_EVENT_LBUTTON; + if (button_mask & 0x02) + buttons |= MOUSE_EVENT_MBUTTON; + if (button_mask & 0x04) + buttons |= MOUSE_EVENT_RBUTTON; + if (button_mask & 0x08) + dz = -1; + if (button_mask & 0x10) + dz = 1; + + if (kbd_mouse_is_absolute()) { + kbd_mouse_event(x * 0x7FFF / vs->ds->width, + y * 0x7FFF / vs->ds->height, + dz, buttons); + } else { + static int last_x = -1; + static int last_y = -1; + + if (last_x != -1) + kbd_mouse_event(x - last_x, y - last_y, dz, buttons); + + last_x = x; + last_y = y; + } +} + +static void do_key_event(VncState *vs, int down, uint32_t sym) +{ + int keycode; + + keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF); + + if (keycode & 0x80) + kbd_put_keycode(0xe0); + if (down) + kbd_put_keycode(keycode & 0x7f); + else + kbd_put_keycode(keycode | 0x80); +} + +static void key_event(VncState *vs, int down, uint32_t sym) +{ + if (sym >= 'A' && sym <= 'Z') + sym = sym - 'A' + 'a'; + do_key_event(vs, down, sym); +} + +static void framebuffer_set_updated(VncState *vs, int x, int y, int w, int h) +{ + + set_bits_in_row(vs, vs->update_row, x, y, w, h); + + vs->has_update = 1; +} + +static void framebuffer_update_request(VncState *vs, int incremental, + int x_position, int y_position, + int w, int h) +{ + vs->need_update = 1; + if (!incremental) + framebuffer_set_updated(vs, x_position, y_position, w, h); + vs->visible_x = x_position; + vs->visible_y = y_position; + vs->visible_w = w; + vs->visible_h = h; +} + +static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) +{ + int i; + + vs->has_hextile = 0; + vs->has_resize = 0; + vs->ds->dpy_copy = NULL; + + for (i = n_encodings - 1; i >= 0; i--) { + switch (encodings[i]) { + case 0: /* Raw */ + vs->has_hextile = 0; + break; + case 1: /* CopyRect */ + vs->ds->dpy_copy = vnc_copy; + break; + case 5: /* Hextile */ + vs->has_hextile = 1; + break; + case -223: /* DesktopResize */ + vs->has_resize = 1; + break; + default: + break; + } + } +} + +static void set_pixel_format(VncState *vs, + int bits_per_pixel, int depth, + int big_endian_flag, int true_color_flag, + int red_max, int green_max, int blue_max, + int red_shift, int green_shift, int blue_shift) +{ + switch (bits_per_pixel) { + case 32: + case 24: + vs->depth = 4; + break; + case 16: + vs->depth = 2; + break; + case 8: + vs->depth = 1; + break; + default: + vnc_client_error(vs); + break; + } + + if (!true_color_flag) + vnc_client_error(vs); + + vnc_dpy_resize(vs->ds, vs->ds->width, vs->ds->height); + + vga_hw_invalidate(); + vga_hw_update(); +} + +static int protocol_client_msg(VncState *vs, char *data, size_t len) +{ + int i; + uint16_t limit; + + switch (data[0]) { + case 0: + if (len == 1) + return 20; + + set_pixel_format(vs, read_u8(data, 4), read_u8(data, 5), + read_u8(data, 6), read_u8(data, 7), + read_u16(data, 8), read_u16(data, 10), + read_u16(data, 12), read_u8(data, 14), + read_u8(data, 15), read_u8(data, 16)); + break; + case 2: + if (len == 1) + return 4; + + if (len == 4) + return 4 + (read_u16(data, 2) * 4); + + limit = read_u16(data, 2); + for (i = 0; i < limit; i++) { + int32_t val = read_s32(data, 4 + (i * 4)); + memcpy(data + 4 + (i * 4), &val, sizeof(val)); + } + + set_encodings(vs, (int32_t *)(data + 4), limit); + break; + case 3: + if (len == 1) + return 10; + + framebuffer_update_request(vs, + read_u8(data, 1), read_u16(data, 2), read_u16(data, 4), + read_u16(data, 6), read_u16(data, 8)); + break; + case 4: + if (len == 1) + return 8; + + key_event(vs, read_u8(data, 1), read_u32(data, 4)); + break; + case 5: + if (len == 1) + return 6; + + pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4)); + break; + case 6: + if (len == 1) + return 8; + + if (len == 8) + return 8 + read_u32(data, 4); + + client_cut_text(vs, read_u32(data, 4), data + 8); + break; + default: + printf("Msg: %d\n", data[0]); + vnc_client_error(vs); + break; + } + + vnc_read_when(vs, protocol_client_msg, 1); + return 0; +} + +static int protocol_client_init(VncState *vs, char *data, size_t len) +{ + char pad[3] = { 0, 0, 0 }; + + vs->width = vs->ds->width; + vs->height = vs->ds->height; + vnc_write_u16(vs, vs->ds->width); + vnc_write_u16(vs, vs->ds->height); + + vnc_write_u8(vs, vs->depth * 8); /* bits-per-pixel */ + vnc_write_u8(vs, vs->depth * 8); /* depth */ + vnc_write_u8(vs, 0); /* big-endian-flag */ + vnc_write_u8(vs, 1); /* true-color-flag */ + if (vs->depth == 4) { + vnc_write_u16(vs, 0xFF); /* red-max */ + vnc_write_u16(vs, 0xFF); /* green-max */ + vnc_write_u16(vs, 0xFF); /* blue-max */ + vnc_write_u8(vs, 16); /* red-shift */ + vnc_write_u8(vs, 8); /* green-shift */ + vnc_write_u8(vs, 0); /* blue-shift */ + } else if (vs->depth == 2) { + vnc_write_u16(vs, 31); /* red-max */ + vnc_write_u16(vs, 63); /* green-max */ + vnc_write_u16(vs, 31); /* blue-max */ + vnc_write_u8(vs, 11); /* red-shift */ + vnc_write_u8(vs, 5); /* green-shift */ + vnc_write_u8(vs, 0); /* blue-shift */ + } else if (vs->depth == 1) { + vnc_write_u16(vs, 3); /* red-max */ + vnc_write_u16(vs, 7); /* green-max */ + vnc_write_u16(vs, 3); /* blue-max */ + vnc_write_u8(vs, 5); /* red-shift */ + vnc_write_u8(vs, 2); /* green-shift */ + vnc_write_u8(vs, 0); /* blue-shift */ + } + + vnc_write(vs, pad, 3); /* padding */ + + vnc_write_u32(vs, 4); + vnc_write(vs, "QEMU", 4); + vnc_flush(vs); + + vnc_read_when(vs, protocol_client_msg, 1); + + return 0; +} + +static int protocol_version(VncState *vs, char *version, size_t len) +{ + char local[13]; + int maj, min; + + memcpy(local, version, 12); + local[12] = 0; + + if (sscanf(local, "RFB %03d.%03d\n", &maj, &min) != 2) { + vnc_client_error(vs); + return 0; + } + + vnc_write_u32(vs, 1); /* None */ + vnc_flush(vs); + + vnc_read_when(vs, protocol_client_init, 1); + + return 0; +} + +static void vnc_listen_read(void *opaque) +{ + VncState *vs = opaque; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + + vs->csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); + if (vs->csock != -1) { + socket_set_nonblock(vs->csock); + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque); + vnc_write(vs, "RFB 003.003\n", 12); + vnc_flush(vs); + vnc_read_when(vs, protocol_version, 12); + framebuffer_set_updated(vs, 0, 0, vs->ds->width, vs->ds->height); + vs->has_resize = 0; + vs->has_hextile = 0; + vs->ds->dpy_copy = NULL; + vnc_timer_init(vs); + } +} + +void vnc_display_init(DisplayState *ds, int display) +{ + struct sockaddr_in addr; + int reuse_addr, ret; + VncState *vs; + + vs = qemu_mallocz(sizeof(VncState)); + if (!vs) + exit(1); + + ds->opaque = vs; + + vs->lsock = -1; + vs->csock = -1; + vs->depth = 4; + + vs->ds = ds; + + if (!keyboard_layout) + keyboard_layout = "en-us"; + + vs->kbd_layout = init_keyboard_layout(keyboard_layout); + if (!vs->kbd_layout) + exit(1); + + vs->lsock = socket(PF_INET, SOCK_STREAM, 0); + if (vs->lsock == -1) { + fprintf(stderr, "Could not create socket\n"); + exit(1); + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(5900 + display); + memset(&addr.sin_addr, 0, sizeof(addr.sin_addr)); + + reuse_addr = 1; + ret = setsockopt(vs->lsock, SOL_SOCKET, SO_REUSEADDR, + (const char *)&reuse_addr, sizeof(reuse_addr)); + if (ret == -1) { + fprintf(stderr, "setsockopt() failed\n"); + exit(1); + } + + if (bind(vs->lsock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + fprintf(stderr, "bind() failed\n"); + exit(1); + } + + if (listen(vs->lsock, 1) == -1) { + fprintf(stderr, "listen() failed\n"); + exit(1); + } + + ret = qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, + NULL, vs); + if (ret == -1) + exit(1); + + vs->ds->data = NULL; + vs->ds->dpy_update = vnc_dpy_update; + vs->ds->dpy_resize = vnc_dpy_resize; + vs->ds->dpy_refresh = vnc_dpy_refresh; + + vnc_dpy_resize(vs->ds, 640, 400); +} diff --git a/tools/ioemu/vnc_keysym.h b/tools/ioemu/vnc_keysym.h new file mode 100644 index 0000000000..a4ac6885bf --- /dev/null +++ b/tools/ioemu/vnc_keysym.h @@ -0,0 +1,275 @@ +typedef struct { + const char* name; + int keysym; +} name2keysym_t; +static name2keysym_t name2keysym[]={ +/* ascii */ + { "space", 0x020}, + { "exclam", 0x021}, + { "quotedbl", 0x022}, + { "numbersign", 0x023}, + { "dollar", 0x024}, + { "percent", 0x025}, + { "ampersand", 0x026}, + { "apostrophe", 0x027}, + { "parenleft", 0x028}, + { "parenright", 0x029}, + { "asterisk", 0x02a}, + { "plus", 0x02b}, + { "comma", 0x02c}, + { "minus", 0x02d}, + { "period", 0x02e}, + { "slash", 0x02f}, + { "0", 0x030}, + { "1", 0x031}, + { "2", 0x032}, + { "3", 0x033}, + { "4", 0x034}, + { "5", 0x035}, + { "6", 0x036}, + { "7", 0x037}, + { "8", 0x038}, + { "9", 0x039}, + { "colon", 0x03a}, + { "semicolon", 0x03b}, + { "less", 0x03c}, + { "equal", 0x03d}, + { "greater", 0x03e}, + { "question", 0x03f}, + { "at", 0x040}, + { "A", 0x041}, + { "B", 0x042}, + { "C", 0x043}, + { "D", 0x044}, + { "E", 0x045}, + { "F", 0x046}, + { "G", 0x047}, + { "H", 0x048}, + { "I", 0x049}, + { "J", 0x04a}, + { "K", 0x04b}, + { "L", 0x04c}, + { "M", 0x04d}, + { "N", 0x04e}, + { "O", 0x04f}, + { "P", 0x050}, + { "Q", 0x051}, + { "R", 0x052}, + { "S", 0x053}, + { "T", 0x054}, + { "U", 0x055}, + { "V", 0x056}, + { "W", 0x057}, + { "X", 0x058}, + { "Y", 0x059}, + { "Z", 0x05a}, + { "bracketleft", 0x05b}, + { "backslash", 0x05c}, + { "bracketright", 0x05d}, + { "asciicircum", 0x05e}, + { "underscore", 0x05f}, + { "grave", 0x060}, + { "a", 0x061}, + { "b", 0x062}, + { "c", 0x063}, + { "d", 0x064}, + { "e", 0x065}, + { "f", 0x066}, + { "g", 0x067}, + { "h", 0x068}, + { "i", 0x069}, + { "j", 0x06a}, + { "k", 0x06b}, + { "l", 0x06c}, + { "m", 0x06d}, + { "n", 0x06e}, + { "o", 0x06f}, + { "p", 0x070}, + { "q", 0x071}, + { "r", 0x072}, + { "s", 0x073}, + { "t", 0x074}, + { "u", 0x075}, + { "v", 0x076}, + { "w", 0x077}, + { "x", 0x078}, + { "y", 0x079}, + { "z", 0x07a}, + { "braceleft", 0x07b}, + { "bar", 0x07c}, + { "braceright", 0x07d}, + { "asciitilde", 0x07e}, + +/* latin 1 extensions */ +{ "nobreakspace", 0x0a0}, +{ "exclamdown", 0x0a1}, +{ "cent", 0x0a2}, +{ "sterling", 0x0a3}, +{ "currency", 0x0a4}, +{ "yen", 0x0a5}, +{ "brokenbar", 0x0a6}, +{ "section", 0x0a7}, +{ "diaeresis", 0x0a8}, +{ "copyright", 0x0a9}, +{ "ordfeminine", 0x0aa}, +{ "guillemotleft", 0x0ab}, +{ "notsign", 0x0ac}, +{ "hyphen", 0x0ad}, +{ "registered", 0x0ae}, +{ "macron", 0x0af}, +{ "degree", 0x0b0}, +{ "plusminus", 0x0b1}, +{ "twosuperior", 0x0b2}, +{ "threesuperior", 0x0b3}, +{ "acute", 0x0b4}, +{ "mu", 0x0b5}, +{ "paragraph", 0x0b6}, +{ "periodcentered", 0x0b7}, +{ "cedilla", 0x0b8}, +{ "onesuperior", 0x0b9}, +{ "masculine", 0x0ba}, +{ "guillemotright", 0x0bb}, +{ "onequarter", 0x0bc}, +{ "onehalf", 0x0bd}, +{ "threequarters", 0x0be}, +{ "questiondown", 0x0bf}, +{ "Agrave", 0x0c0}, +{ "Aacute", 0x0c1}, +{ "Acircumflex", 0x0c2}, +{ "Atilde", 0x0c3}, +{ "Adiaeresis", 0x0c4}, +{ "Aring", 0x0c5}, +{ "AE", 0x0c6}, +{ "Ccedilla", 0x0c7}, +{ "Egrave", 0x0c8}, +{ "Eacute", 0x0c9}, +{ "Ecircumflex", 0x0ca}, +{ "Ediaeresis", 0x0cb}, +{ "Igrave", 0x0cc}, +{ "Iacute", 0x0cd}, +{ "Icircumflex", 0x0ce}, +{ "Idiaeresis", 0x0cf}, +{ "ETH", 0x0d0}, +{ "Eth", 0x0d0}, +{ "Ntilde", 0x0d1}, +{ "Ograve", 0x0d2}, +{ "Oacute", 0x0d3}, +{ "Ocircumflex", 0x0d4}, +{ "Otilde", 0x0d5}, +{ "Odiaeresis", 0x0d6}, +{ "multiply", 0x0d7}, +{ "Ooblique", 0x0d8}, +{ "Oslash", 0x0d8}, +{ "Ugrave", 0x0d9}, +{ "Uacute", 0x0da}, +{ "Ucircumflex", 0x0db}, +{ "Udiaeresis", 0x0dc}, +{ "Yacute", 0x0dd}, +{ "THORN", 0x0de}, +{ "Thorn", 0x0de}, +{ "ssharp", 0x0df}, +{ "agrave", 0x0e0}, +{ "aacute", 0x0e1}, +{ "acircumflex", 0x0e2}, +{ "atilde", 0x0e3}, +{ "adiaeresis", 0x0e4}, +{ "aring", 0x0e5}, +{ "ae", 0x0e6}, +{ "ccedilla", 0x0e7}, +{ "egrave", 0x0e8}, +{ "eacute", 0x0e9}, +{ "ecircumflex", 0x0ea}, +{ "ediaeresis", 0x0eb}, +{ "igrave", 0x0ec}, +{ "iacute", 0x0ed}, +{ "icircumflex", 0x0ee}, +{ "idiaeresis", 0x0ef}, +{ "eth", 0x0f0}, +{ "ntilde", 0x0f1}, +{ "ograve", 0x0f2}, +{ "oacute", 0x0f3}, +{ "ocircumflex", 0x0f4}, +{ "otilde", 0x0f5}, +{ "odiaeresis", 0x0f6}, +{ "division", 0x0f7}, +{ "oslash", 0x0f8}, +{ "ooblique", 0x0f8}, +{ "ugrave", 0x0f9}, +{ "uacute", 0x0fa}, +{ "ucircumflex", 0x0fb}, +{ "udiaeresis", 0x0fc}, +{ "yacute", 0x0fd}, +{ "thorn", 0x0fe}, +{ "ydiaeresis", 0x0ff}, +{"EuroSign", 0x20ac}, /* XK_EuroSign */ + + /* modifiers */ +{"Control_L", 0xffe3}, /* XK_Control_L */ +{"Control_R", 0xffe4}, /* XK_Control_R */ +{"Alt_L", 0xffe9}, /* XK_Alt_L */ +{"Alt_R", 0xffea}, /* XK_Alt_R */ +{"Caps_Lock", 0xffe5}, /* XK_Caps_Lock */ +{"Meta_L", 0xffe7}, /* XK_Meta_L */ +{"Meta_R", 0xffe8}, /* XK_Meta_R */ +{"Shift_L", 0xffe1}, /* XK_Shift_L */ +{"Shift_R", 0xffe2}, /* XK_Shift_R */ +{"Super_L", 0xffeb}, /* XK_Super_L */ +{"Super_R", 0xffec}, /* XK_Super_R */ + + /* special keys */ +{"BackSpace", 0xff08}, /* XK_BackSpace */ +{"Tab", 0xff09}, /* XK_Tab */ +{"Return", 0xff0d}, /* XK_Return */ +{"Right", 0xff53}, /* XK_Right */ +{"Left", 0xff51}, /* XK_Left */ +{"Up", 0xff52}, /* XK_Up */ +{"Down", 0xff54}, /* XK_Down */ +{"Page_Down", 0xff56}, /* XK_Page_Down */ +{"Page_Up", 0xff55}, /* XK_Page_Up */ +{"Insert", 0xff63}, /* XK_Insert */ +{"Delete", 0xffff}, /* XK_Delete */ +{"Home", 0xff50}, /* XK_Home */ +{"End", 0xff57}, /* XK_End */ +{"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */ +{"F1", 0xffbe}, /* XK_F1 */ +{"F2", 0xffbf}, /* XK_F2 */ +{"F3", 0xffc0}, /* XK_F3 */ +{"F4", 0xffc1}, /* XK_F4 */ +{"F5", 0xffc2}, /* XK_F5 */ +{"F6", 0xffc3}, /* XK_F6 */ +{"F7", 0xffc4}, /* XK_F7 */ +{"F8", 0xffc5}, /* XK_F8 */ +{"F9", 0xffc6}, /* XK_F9 */ +{"F10", 0xffc7}, /* XK_F10 */ +{"F11", 0xffc8}, /* XK_F11 */ +{"F12", 0xffc9}, /* XK_F12 */ +{"F13", 0xffca}, /* XK_F13 */ +{"F14", 0xffcb}, /* XK_F14 */ +{"F15", 0xffcc}, /* XK_F15 */ +{"Sys_Req", 0xff15}, /* XK_Sys_Req */ +{"KP_0", 0xffb0}, /* XK_KP_0 */ +{"KP_1", 0xffb1}, /* XK_KP_1 */ +{"KP_2", 0xffb2}, /* XK_KP_2 */ +{"KP_3", 0xffb3}, /* XK_KP_3 */ +{"KP_4", 0xffb4}, /* XK_KP_4 */ +{"KP_5", 0xffb5}, /* XK_KP_5 */ +{"KP_6", 0xffb6}, /* XK_KP_6 */ +{"KP_7", 0xffb7}, /* XK_KP_7 */ +{"KP_8", 0xffb8}, /* XK_KP_8 */ +{"KP_9", 0xffb9}, /* XK_KP_9 */ +{"KP_Add", 0xffab}, /* XK_KP_Add */ +{"KP_Decimal", 0xffae}, /* XK_KP_Decimal */ +{"KP_Divide", 0xffaf}, /* XK_KP_Divide */ +{"KP_Enter", 0xff8d}, /* XK_KP_Enter */ +{"KP_Equal", 0xffbd}, /* XK_KP_Equal */ +{"KP_Multiply", 0xffaa}, /* XK_KP_Multiply */ +{"KP_Subtract", 0xffad}, /* XK_KP_Subtract */ +{"help", 0xff6a}, /* XK_Help */ +{"Menu", 0xff67}, /* XK_Menu */ +{"Print", 0xff61}, /* XK_Print */ +{"Mode_switch", 0xff7e}, /* XK_Mode_switch */ +{"Num_Lock", 0xff7f}, /* XK_Num_Lock */ +{"Pause", 0xff13}, /* XK_Pause */ +{"Escape", 0xff1b}, /* XK_Escape */ +{0,0}, +}; diff --git a/tools/ioemu/vnchextile.h b/tools/ioemu/vnchextile.h new file mode 100644 index 0000000000..7277670a56 --- /dev/null +++ b/tools/ioemu/vnchextile.h @@ -0,0 +1,189 @@ +#define CONCAT_I(a, b) a ## b +#define CONCAT(a, b) CONCAT_I(a, b) +#define pixel_t CONCAT(uint, CONCAT(BPP, _t)) + +static void CONCAT(send_hextile_tile_, BPP)(VncState *vs, + int x, int y, int w, int h, + pixel_t *last_bg, pixel_t *last_fg, + int *has_bg, int *has_fg) +{ + char *row = (vs->ds->data + y * vs->ds->linesize + x * vs->depth); + pixel_t *irow = (pixel_t *)row; + int j, i; + pixel_t bg = 0; + pixel_t fg = 0; + int n_colors = 0; + int bg_count = 0; + int fg_count = 0; + int flags = 0; + uint8_t data[(sizeof(pixel_t) + 2) * 16 * 16]; + int n_data = 0; + int n_subtiles = 0; + + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++) { + switch (n_colors) { + case 0: + bg = irow[i]; + n_colors = 1; + break; + case 1: + if (irow[i] != bg) { + fg = irow[i]; + n_colors = 2; + } + break; + case 2: + if (irow[i] != bg && irow[i] != fg) { + n_colors = 3; + } else { + if (irow[i] == bg) + bg_count++; + else if (irow[i] == fg) + fg_count++; + } + break; + default: + break; + } + } + if (n_colors > 2) + break; + irow += vs->ds->linesize / sizeof(pixel_t); + } + + if (n_colors > 1 && fg_count > bg_count) { + pixel_t tmp = fg; + fg = bg; + bg = tmp; + } + + if (!*has_bg || *last_bg != bg) { + flags |= 0x02; + *has_bg = 1; + *last_bg = bg; + } + + if (!*has_fg || *last_fg != fg) { + flags |= 0x04; + *has_fg = 1; + *last_fg = fg; + } + + switch (n_colors) { + case 1: + n_data = 0; + break; + case 2: + flags |= 0x08; + + irow = (pixel_t *)row; + + for (j = 0; j < h; j++) { + int min_x = -1; + for (i = 0; i < w; i++) { + if (irow[i] == fg) { + if (min_x == -1) + min_x = i; + } else if (min_x != -1) { + hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); + n_data += 2; + n_subtiles++; + min_x = -1; + } + } + if (min_x != -1) { + hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); + n_data += 2; + n_subtiles++; + } + irow += vs->ds->linesize / sizeof(pixel_t); + } + break; + case 3: + flags |= 0x18; + + irow = (pixel_t *)row; + + if (!*has_bg || *last_bg != bg) + flags |= 0x02; + + for (j = 0; j < h; j++) { + int has_color = 0; + int min_x = -1; + pixel_t color; + + for (i = 0; i < w; i++) { + if (!has_color) { + if (irow[i] == bg) + continue; + color = irow[i]; + min_x = i; + has_color = 1; + } else if (irow[i] != color) { + has_color = 0; + + memcpy(data + n_data, &color, sizeof(color)); + hextile_enc_cord(data + n_data + sizeof(pixel_t), min_x, j, i - min_x, 1); + n_data += 2 + sizeof(pixel_t); + n_subtiles++; + + min_x = -1; + if (irow[i] != bg) { + color = irow[i]; + min_x = i; + has_color = 1; + } + } + } + if (has_color) { + memcpy(data + n_data, &color, sizeof(color)); + hextile_enc_cord(data + n_data + sizeof(pixel_t), min_x, j, i - min_x, 1); + n_data += 2 + sizeof(pixel_t); + n_subtiles++; + } + irow += vs->ds->linesize / sizeof(pixel_t); + } + + /* A SubrectsColoured subtile invalidates the foreground color */ + *has_fg = 0; + if (n_data > (w * h * sizeof(pixel_t))) { + n_colors = 4; + flags = 0x01; + *has_bg = 0; + + /* we really don't have to invalidate either the bg or fg + but we've lost the old values. oh well. */ + } + default: + break; + } + + if (n_colors > 3) { + flags = 0x01; + *has_fg = 0; + *has_bg = 0; + n_colors = 4; + } + + vnc_write_u8(vs, flags); + if (n_colors < 4) { + if (flags & 0x02) + vnc_write(vs, last_bg, sizeof(pixel_t)); + if (flags & 0x04) + vnc_write(vs, last_fg, sizeof(pixel_t)); + if (n_subtiles) { + vnc_write_u8(vs, n_subtiles); + vnc_write(vs, data, n_data); + } + } else { + for (j = 0; j < h; j++) { + vnc_write(vs, row, w * vs->depth); + row += vs->ds->linesize; + } + } +} + +#undef pixel_t +#undef CONCAT_I +#undef CONCAT diff --git a/tools/ioemu/x86_64.ld b/tools/ioemu/x86_64.ld new file mode 100644 index 0000000000..878dafbe79 --- /dev/null +++ b/tools/ioemu/x86_64.ld @@ -0,0 +1,171 @@ +/* Default linker script, for normal executables */ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) +SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/local/lib64"); +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0x60000000 + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) } + .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) } + .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) } + .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) } + .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) } + .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) } + .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) } + .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) } + .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) } + .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : + { + KEEP (*(.init)) + } =0x90909090 + .plt : { *(.plt) } + .text : + { + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + } =0x90909090 + .fini : + { + KEEP (*(.fini)) + } =0x90909090 + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .eh_frame_hdr : { *(.eh_frame_hdr) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN (0x100000) - ((0x100000 - .) & (0x100000 - 1)); . = DATA_SEGMENT_ALIGN (0x100000, 0x1000); + /* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(64 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array : { *(.preinit_array) } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { *(.init_array) } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { *(.fini_array) } + PROVIDE (__fini_array_end = .); + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table) } + .dynamic : { *(.dynamic) } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + /* We don't want to include the .ctor section from + from the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .got : { *(.got.plt) *(.got) } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + . = ALIGN(64 / 8); + } + . = ALIGN(64 / 8); + _end = .; + PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +}